<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<meta name="theme-color" content="#222"><meta name="generator" content="Hexo 7.3.0">

  <link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png">
  <link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32.ico">
  <link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16.ico">
  <link rel="mask-icon" href="/images/logo.svg" color="#222">

<link rel="stylesheet" href="/css/main.css">



<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css" integrity="sha256-wiz7ZSCn/btzhjKDQBms9Hx4sSeUYsDrTLg7roPstac=" crossorigin="anonymous">
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.1.1/animate.min.css" integrity="sha256-PR7ttpcvz8qrF57fur/yAx1qXMFJeJFiA6pSzWi0OIE=" crossorigin="anonymous">
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/fancyapps-ui/5.0.28/fancybox/fancybox.css" integrity="sha256-6cQIC71/iBIYXFK+0RHAvwmjwWzkWd+r7v/BX3/vZDc=" crossorigin="anonymous">
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/pace/1.2.4/themes/green/pace-theme-minimal.css">
  <script src="https://cdnjs.cloudflare.com/ajax/libs/pace/1.2.4/pace.min.js" integrity="sha256-gqd7YTjg/BtfqWSwsJOvndl0Bxc8gFImLEkXQT8+qj0=" crossorigin="anonymous"></script>

<script class="next-config" data-name="main" type="application/json">{"hostname":"sumumm.github.io","root":"/","images":"/images","scheme":"Gemini","darkmode":false,"version":"8.19.2","exturl":false,"sidebar":{"position":"left","display":"post","padding":18,"offset":12},"copycode":{"enable":true,"style":"mac"},"fold":{"enable":true,"height":300},"bookmark":{"enable":false,"color":"#222","save":"auto"},"mediumzoom":false,"lazyload":true,"pangu":false,"comments":{"style":"tabs","active":null,"storage":true,"lazyload":false,"nav":null},"stickytabs":false,"motion":{"enable":true,"async":true,"transition":{"menu_item":"fadeInDown","post_block":"fadeIn","post_header":"fadeInDown","post_body":"fadeInDown","coll_header":"fadeInLeft","sidebar":"fadeInUp"}},"i18n":{"placeholder":"搜索...","empty":"没有找到任何搜索结果：${query}","hits_time":"找到 ${hits} 个搜索结果（用时 ${time} 毫秒）","hits":"找到 ${hits} 个搜索结果"},"path":"/search.xml","localsearch":{"enable":true,"trigger":"auto","top_n_per_article":1,"unescape":false,"preload":false}}</script><script src="/js/config.js"></script>

    <meta name="description" content="本文主要是uboot启动流程的相关笔记。若笔记中有错误或者不合适的地方，欢迎批评指正😃。">
<meta property="og:type" content="article">
<meta property="og:title" content="LV05-01-uboot-04-uboot启动流程">
<meta property="og:url" content="https://sumumm.github.io/post/e99b9cbc.html">
<meta property="og:site_name" content="苏木">
<meta property="og:description" content="本文主要是uboot启动流程的相关笔记。若笔记中有错误或者不合适的地方，欢迎批评指正😃。">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV05-uboot%E4%B8%8E%E5%86%85%E6%A0%B8/LV05-01-uboot-04-uboot%E5%90%AF%E5%8A%A8%E6%B5%81%E7%A8%8B/img/image-20221015091825040.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV05-uboot%E4%B8%8E%E5%86%85%E6%A0%B8/LV05-01-uboot-04-uboot%E5%90%AF%E5%8A%A8%E6%B5%81%E7%A8%8B/img/image-20221015092906081.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV05-uboot%E4%B8%8E%E5%86%85%E6%A0%B8/LV05-01-uboot-04-uboot%E5%90%AF%E5%8A%A8%E6%B5%81%E7%A8%8B/img/image-20221015130030906.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV05-uboot%E4%B8%8E%E5%86%85%E6%A0%B8/LV05-01-uboot-04-uboot%E5%90%AF%E5%8A%A8%E6%B5%81%E7%A8%8B/img/image-20221015130125594.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV05-uboot%E4%B8%8E%E5%86%85%E6%A0%B8/LV05-01-uboot-04-uboot%E5%90%AF%E5%8A%A8%E6%B5%81%E7%A8%8B/img/image-20221015143824906.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV05-uboot%E4%B8%8E%E5%86%85%E6%A0%B8/LV05-01-uboot-04-uboot%E5%90%AF%E5%8A%A8%E6%B5%81%E7%A8%8B/img/image-20221015165952642.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV05-uboot%E4%B8%8E%E5%86%85%E6%A0%B8/LV05-01-uboot-04-uboot%E5%90%AF%E5%8A%A8%E6%B5%81%E7%A8%8B/img/image-20221015170028569.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV05-uboot%E4%B8%8E%E5%86%85%E6%A0%B8/LV05-01-uboot-04-uboot%E5%90%AF%E5%8A%A8%E6%B5%81%E7%A8%8B/img/image-20221015170057791.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV05-uboot%E4%B8%8E%E5%86%85%E6%A0%B8/LV05-01-uboot-04-uboot%E5%90%AF%E5%8A%A8%E6%B5%81%E7%A8%8B/img/image-20221015170124394.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV05-uboot%E4%B8%8E%E5%86%85%E6%A0%B8/LV05-01-uboot-04-uboot%E5%90%AF%E5%8A%A8%E6%B5%81%E7%A8%8B/img/image-20221015170554062.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV05-uboot%E4%B8%8E%E5%86%85%E6%A0%B8/LV05-01-uboot-04-uboot%E5%90%AF%E5%8A%A8%E6%B5%81%E7%A8%8B/img/image-20221015171129255.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV05-uboot%E4%B8%8E%E5%86%85%E6%A0%B8/LV05-01-uboot-04-uboot%E5%90%AF%E5%8A%A8%E6%B5%81%E7%A8%8B/img/image-20221015203536954.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV05-uboot%E4%B8%8E%E5%86%85%E6%A0%B8/LV05-01-uboot-04-uboot%E5%90%AF%E5%8A%A8%E6%B5%81%E7%A8%8B/img/image-20221015210430581.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV05-uboot%E4%B8%8E%E5%86%85%E6%A0%B8/LV05-01-uboot-04-uboot%E5%90%AF%E5%8A%A8%E6%B5%81%E7%A8%8B/img/image-20221016083509867.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV05-uboot%E4%B8%8E%E5%86%85%E6%A0%B8/LV05-01-uboot-04-uboot%E5%90%AF%E5%8A%A8%E6%B5%81%E7%A8%8B/img/image-20221016083629656.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV05-uboot%E4%B8%8E%E5%86%85%E6%A0%B8/LV05-01-uboot-04-uboot%E5%90%AF%E5%8A%A8%E6%B5%81%E7%A8%8B/img/image-20221016083741446.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV05-uboot%E4%B8%8E%E5%86%85%E6%A0%B8/LV05-01-uboot-04-uboot%E5%90%AF%E5%8A%A8%E6%B5%81%E7%A8%8B/img/image-20221016084737899.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV05-uboot%E4%B8%8E%E5%86%85%E6%A0%B8/LV05-01-uboot-04-uboot%E5%90%AF%E5%8A%A8%E6%B5%81%E7%A8%8B/img/image-20221016084922548.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV05-uboot%E4%B8%8E%E5%86%85%E6%A0%B8/LV05-01-uboot-04-uboot%E5%90%AF%E5%8A%A8%E6%B5%81%E7%A8%8B/img/image-20221016100037636.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV05-uboot%E4%B8%8E%E5%86%85%E6%A0%B8/LV05-01-uboot-04-uboot%E5%90%AF%E5%8A%A8%E6%B5%81%E7%A8%8B/img/image-20221016101203235.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV05-uboot%E4%B8%8E%E5%86%85%E6%A0%B8/LV05-01-uboot-04-uboot%E5%90%AF%E5%8A%A8%E6%B5%81%E7%A8%8B/img/image-20221016113057647.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV05-uboot%E4%B8%8E%E5%86%85%E6%A0%B8/LV05-01-uboot-04-uboot%E5%90%AF%E5%8A%A8%E6%B5%81%E7%A8%8B/img/uboot_start-169061995797628.png">
<meta property="article:published_time" content="2023-09-08T11:22:56.000Z">
<meta property="article:modified_time" content="2025-06-13T16:25:57.041Z">
<meta property="article:author" content="苏木">
<meta property="article:tag" content="(ALPHA)LV05-uboot与内核">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV05-uboot%E4%B8%8E%E5%86%85%E6%A0%B8/LV05-01-uboot-04-uboot%E5%90%AF%E5%8A%A8%E6%B5%81%E7%A8%8B/img/image-20221015091825040.png">


<link rel="canonical" href="https://sumumm.github.io/post/e99b9cbc.html">



<script class="next-config" data-name="page" type="application/json">{"sidebar":"","isHome":false,"isPost":true,"lang":"zh-CN","comments":true,"permalink":"https://sumumm.github.io/post/e99b9cbc.html","path":"post/e99b9cbc.html","title":"LV05-01-uboot-04-uboot启动流程"}</script>

<script class="next-config" data-name="calendar" type="application/json">""</script>
<title>LV05-01-uboot-04-uboot启动流程 | 苏木</title>
  








    <script src="/js/browser_tools_disable.js"></script>

  <noscript>
    <link rel="stylesheet" href="/css/noscript.css">
  </noscript>
<!-- hexo injector head_end start --><link rel="stylesheet" href="https://unpkg.com/hexo-next-tags-plus@latest/lib/tag_plus.css" media="defer" onload="this.media='all'"><!-- hexo injector head_end end --></head>

<body itemscope itemtype="http://schema.org/WebPage" class="use-motion">
  <div class="headband"></div>

  <main class="main">
    <div class="column">
      <header class="header" itemscope itemtype="http://schema.org/WPHeader"><div class="site-brand-container">
  <div class="site-nav-toggle">
    <div class="toggle" aria-label="切换导航栏" role="button">
        <span class="toggle-line"></span>
        <span class="toggle-line"></span>
        <span class="toggle-line"></span>
    </div>
  </div>

  <div class="site-meta">

    <a href="/" class="brand" rel="start">
      <i class="logo-line"></i>
      <p class="site-title">苏木</p>
      <i class="logo-line"></i>
    </a>
      <p class="site-subtitle" itemprop="description">我的学习之路</p>
  </div>

  <div class="site-nav-right">
    <div class="toggle popup-trigger" aria-label="搜索" role="button">
        <i class="fa fa-search fa-fw fa-lg"></i>
    </div>
  </div>
</div>



<nav class="site-nav">
  <ul class="main-menu menu"><li class="menu-item menu-item-home"><a href="/" rel="section"><i class="fa fa-home fa-fw"></i>苏木的家</a></li><li class="menu-item menu-item-categories"><a href="/categories/" rel="section"><i class="fa fa-th fa-fw"></i>分类页<span class="badge">42</span></a></li><li class="menu-item menu-item-archives"><a href="/archives/" rel="section"><i class="fa fa-archive fa-fw"></i>归档页<span class="badge">673</span></a></li><li class="menu-item menu-item-flink"><a href="/flink/" rel="section"><i class="fa fa-link fa-fw"></i>友人帐</a></li><li class="menu-item menu-item-about"><a href="/about/" rel="section"><i class="fa fa-user fa-fw"></i>关于我</a></li>
      <li class="menu-item menu-item-search">
        <a role="button" class="popup-trigger"><i class="fa fa-search fa-fw"></i>搜索
        </a>
      </li>
  </ul>
</nav>



  <div class="search-pop-overlay">
    <div class="popup search-popup"><div class="search-header">
  <span class="search-icon">
    <i class="fa fa-search"></i>
  </span>
  <div class="search-input-container">
    <input autocomplete="off" autocapitalize="off" maxlength="80"
           placeholder="搜索..." spellcheck="false"
           type="search" class="search-input">
  </div>
  <span class="popup-btn-close" role="button">
    <i class="fa fa-times-circle"></i>
  </span>
</div>
<div class="search-result-container no-result">
  <div class="search-result-icon">
    <i class="fa fa-spinner fa-pulse fa-5x"></i>
  </div>
</div>

    </div>
  </div>

</header>
        
  
  <aside class="sidebar">

    <div class="sidebar-inner sidebar-nav-active sidebar-toc-active">
      <ul class="sidebar-nav">
        <li class="sidebar-nav-toc">
          文章目录
        </li>
        <li class="sidebar-nav-overview">
          站点概览
        </li>
      </ul>

      <div class="sidebar-panel-container">
        <!--noindex-->
        <div class="post-toc-wrap sidebar-panel">
            <div class="post-toc animated"><ol class="nav"><li class="nav-item nav-level-1"><a class="nav-link" href="#%E4%B8%80%E3%80%81%E9%93%BE%E6%8E%A5%E8%84%9A%E6%9C%ACu-boot-lds"><span class="nav-text">一、链接脚本u-boot.lds  </span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#1-%E7%A8%8B%E5%BA%8F%E5%85%A5%E5%8F%A3"><span class="nav-text">1. 程序入口</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#2-uboot%E8%B5%B7%E5%A7%8B%E5%9C%B0%E5%9D%80"><span class="nav-text">2. uboot起始地址</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#3-%E4%B8%8E%E5%9C%B0%E5%9D%80%E7%9B%B8%E5%85%B3%E7%9A%84%E5%8F%98%E9%87%8F"><span class="nav-text">3. 与地址相关的变量</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#%E4%BA%8C%E3%80%81uboot%E5%90%AF%E5%8A%A8%E6%B5%81%E7%A8%8B"><span class="nav-text">二、uboot启动流程</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#1-reset-%E5%87%BD%E6%95%B0"><span class="nav-text">1. reset 函数</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#1-1-reset-%E5%87%BD%E6%95%B0%E5%9C%A8%E5%93%AA%EF%BC%9F"><span class="nav-text">1.1 reset 函数在哪？</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#1-2-%E6%9C%80%E7%BB%88%E5%81%9A%E4%BA%86%E4%BB%80%E4%B9%88%EF%BC%9F"><span class="nav-text">1.2 最终做了什么？</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#1-2-1-%E8%AE%BE%E7%BD%AE%E5%B7%A5%E4%BD%9C%E7%8A%B6%E6%80%81"><span class="nav-text">1.2.1 设置工作状态</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#1-2-2-%E8%AE%BE%E7%BD%AE%E5%90%91%E9%87%8F%E8%A1%A8"><span class="nav-text">1.2.2 设置向量表</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#1-2-3-%E6%9C%80%E7%BB%88%E8%B7%B3%E8%BD%AC%E5%88%B0-main"><span class="nav-text">1.2.3 最终跳转到 main</span></a></li></ol></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#2-lowlevel-init-%E5%87%BD%E6%95%B0"><span class="nav-text">2. lowlevel_init 函数</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#2-1-lowlevel-init-%E5%87%BD%E6%95%B0%E5%9C%A8%E5%93%AA%EF%BC%9F"><span class="nav-text">2.1 lowlevel_init 函数在哪？</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#2-2-%E6%9C%80%E7%BB%88%E5%81%9A%E4%BA%86%E4%BB%80%E4%B9%88%EF%BC%9F"><span class="nav-text">2.2 最终做了什么？</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#2-1-%E8%AE%BE%E7%BD%AE%E5%A0%86%E6%A0%88%E6%8C%87%E9%92%88-sp"><span class="nav-text">2.1 设置堆栈指针 sp</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#2-2-sp%E6%8C%87%E9%92%888%E5%AD%97%E8%8A%82%E5%AF%B9%E9%BD%90"><span class="nav-text">2.2 sp指针8字节对齐</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#2-3-sp%E6%8C%87%E9%92%88%E5%85%B6%E4%BB%96%E6%93%8D%E4%BD%9C"><span class="nav-text">2.3 sp指针其他操作</span></a></li></ol></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#3-s-init-%E5%87%BD%E6%95%B0"><span class="nav-text">3. s_init 函数</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#3-1-s-init-%E5%87%BD%E6%95%B0%E5%9C%A8%E5%93%AA%EF%BC%9F"><span class="nav-text">3.1 s_init 函数在哪？</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#3-2-%E6%9C%80%E7%BB%88%E5%81%9A%E4%BA%86%E4%BB%80%E4%B9%88%EF%BC%9F"><span class="nav-text">3.2 最终做了什么？</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#4-main-%E5%87%BD%E6%95%B0"><span class="nav-text">4. _main 函数</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#4-1-main-%E5%87%BD%E6%95%B0%E5%9C%A8%E5%93%AA%E9%87%8C%EF%BC%9F"><span class="nav-text">4.1 _main 函数在哪里？</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#4-2-%E5%81%9A%E4%BA%86%E4%BB%80%E4%B9%88%EF%BC%9F"><span class="nav-text">4.2 做了什么？</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#4-2-1-%E8%AE%BE%E7%BD%AE%E5%88%9D%E5%A7%8BC%E8%BF%90%E8%A1%8C%E6%97%B6%E7%8E%AF%E5%A2%83"><span class="nav-text">4.2.1 设置初始C运行时环境</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#4-2-2-%E8%AE%BE%E7%BD%AE%E4%B8%AD%E9%97%B4%E7%8E%AF%E5%A2%83"><span class="nav-text">4.2.2 设置中间环境</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#4-2-3-%E8%AE%BE%E7%BD%AE%E6%9C%80%E7%BB%88%E7%8E%AF%E5%A2%83"><span class="nav-text">4.2.3 设置最终环境</span></a></li></ol></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#5-board-init-f-%E5%87%BD%E6%95%B0"><span class="nav-text">5. board_init_f 函数</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#5-1-board-init-f-%E5%87%BD%E6%95%B0%E5%9C%A8%E5%93%AA%E9%87%8C%EF%BC%9F"><span class="nav-text">5.1 board_init_f 函数在哪里？</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#5-2-%E5%81%9A%E4%BA%86%E4%BB%80%E4%B9%88%EF%BC%9F"><span class="nav-text">5.2 做了什么？</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#5-3-init-sequence-f"><span class="nav-text">5.3 init_sequence_f</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#5-3-1-%E6%95%B0%E7%BB%84%E6%88%90%E5%91%98"><span class="nav-text">5.3.1 数组成员</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#5-3-2-%E9%87%8D%E8%A6%81%E5%87%BD%E6%95%B0%E8%AF%B4%E6%98%8E"><span class="nav-text">5.3.2 重要函数说明</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#5-3-3-%E6%9C%80%E7%BB%88%E5%86%85%E5%AD%98%E5%88%86%E9%85%8D"><span class="nav-text">5.3.3 最终内存分配</span></a></li></ol></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#6-relocate-code-%E5%87%BD%E6%95%B0"><span class="nav-text">6. relocate_code 函数</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#6-1-relocate-code-%E5%87%BD%E6%95%B0%E5%9C%A8%E5%93%AA%E9%87%8C%EF%BC%9F"><span class="nav-text">6.1 relocate_code 函数在哪里？</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#6-2-%E5%81%9A%E4%BA%86%E4%BB%80%E4%B9%88%EF%BC%9F"><span class="nav-text">6.2 做了什么？</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#7-relocate-vectors%E5%87%BD%E6%95%B0"><span class="nav-text">7. relocate_vectors函数</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#7-1-relocate-vectors-%E5%87%BD%E6%95%B0%E5%9C%A8%E5%93%AA%E9%87%8C%EF%BC%9F"><span class="nav-text">7.1 relocate_vectors 函数在哪里？</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#7-2-%E5%81%9A%E4%BA%86%E4%BB%80%E4%B9%88%EF%BC%9F"><span class="nav-text">7.2 做了什么？</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#8-board-init-r-%E5%87%BD%E6%95%B0"><span class="nav-text">8. board_init_r    函数</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#8-1-board-init-r-%E5%87%BD%E6%95%B0%E5%9C%A8%E5%93%AA%E9%87%8C%EF%BC%9F"><span class="nav-text">8.1 board_init_r 函数在哪里？</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#8-2-%E5%81%9A%E4%BA%86%E4%BB%80%E4%B9%88%EF%BC%9F"><span class="nav-text">8.2 做了什么？</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#8-3-init-sequence-r"><span class="nav-text">8.3 init_sequence_r </span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#8-3-1-%E6%95%B0%E7%BB%84%E6%88%90%E5%91%98"><span class="nav-text">8.3.1 数组成员</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#8-3-2-%E9%87%8D%E8%A6%81%E5%87%BD%E6%95%B0%E8%AF%B4%E6%98%8E"><span class="nav-text">8.3.2 重要函数说明</span></a></li></ol></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#9-run-main-loop-%E5%87%BD%E6%95%B0"><span class="nav-text">9. run_main_loop 函数</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#9-1-run-main-loop-%E5%87%BD%E6%95%B0%E5%9C%A8%E5%93%AA%EF%BC%9F"><span class="nav-text">9.1 run_main_loop 函数在哪？</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#9-2-%E5%81%9A%E4%BA%86%E4%BB%80%E4%B9%88%EF%BC%9F"><span class="nav-text">9.2 做了什么？</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#9-2-1-run-main-loop%E5%87%BD%E6%95%B0%E5%AE%9A%E4%B9%89"><span class="nav-text">9.2.1 run_main_loop函数定义</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#9-2-2-main-loop-%E5%87%BD%E6%95%B0"><span class="nav-text">9.2.2 main_loop 函数</span></a></li></ol></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#10-cli-loop-%E5%87%BD%E6%95%B0"><span class="nav-text">10. cli_loop 函数</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#10-1-cli-loop-%E5%87%BD%E6%95%B0%E5%9C%A8%E5%93%AA%EF%BC%9F"><span class="nav-text">10.1 cli_loop 函数在哪？</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#10-2-%E5%81%9A%E4%BA%86%E4%BB%80%E4%B9%88%EF%BC%9F"><span class="nav-text">10.2 做了什么？</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#10-2-1-cli-loop"><span class="nav-text">10.2.1 cli_loop</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#10-2-2-parse-file-outer-%E5%87%BD%E6%95%B0"><span class="nav-text">10.2.2 parse_file_outer 函数</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#10-2-3-parse-stream-outer%E5%87%BD%E6%95%B0"><span class="nav-text">10.2.3 parse_stream_outer函数</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#10-2-4-run-list-%E5%87%BD%E6%95%B0"><span class="nav-text">10.2.4 run_list 函数</span></a></li></ol></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#11-cmd-process-%E5%87%BD%E6%95%B0"><span class="nav-text">11. cmd_process 函数</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#11-1-%E5%91%BD%E4%BB%A4%E7%9A%84%E5%AE%9A%E4%B9%89%E4%B8%8E%E6%89%A7%E8%A1%8C"><span class="nav-text">11.1 命令的定义与执行</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#11-1-1-%E7%9B%B8%E5%85%B3%E5%AE%8F%E5%AE%9A%E4%B9%89"><span class="nav-text">11.1.1 相关宏定义</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#11-1-2-%E5%91%BD%E4%BB%A4%E5%AE%9E%E4%BE%8B"><span class="nav-text">11.1.2 命令实例</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#11-2-3-%E5%91%BD%E4%BB%A4%E6%89%A7%E8%A1%8C"><span class="nav-text">11.2.3 命令执行</span></a></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#11-2-cmd-process-%E5%87%BD%E6%95%B0%E5%9C%A8%E5%93%AA%EF%BC%9F"><span class="nav-text">11.2 cmd_process 函数在哪？</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#11-3-%E5%81%9A%E4%BA%86%E4%BB%80%E4%B9%88%EF%BC%9F"><span class="nav-text">11.3 做了什么？</span></a></li></ol></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#%E4%B8%89%E3%80%81uboot%E5%90%AF%E5%8A%A8%E6%80%BB%E7%BB%93"><span class="nav-text">三、uboot启动总结</span></a></li></ol></div>
        </div>
        <!--/noindex-->

        <div class="site-overview-wrap sidebar-panel">
          <div class="site-author animated" itemprop="author" itemscope itemtype="http://schema.org/Person">
    <img class="site-author-image" itemprop="image" alt="苏木"
      src="/images/avatar.jpg">
  <p class="site-author-name" itemprop="name">苏木</p>
  <div class="site-description" itemprop="description">莫道桑榆晚，为霞尚满天</div>
</div>
<div class="site-state-wrap animated">
  <nav class="site-state">
      <div class="site-state-item site-state-posts">
        <a href="/archives/">
          <span class="site-state-item-count">673</span>
          <span class="site-state-item-name">日志</span>
        </a>
      </div>
      <div class="site-state-item site-state-categories">
          <a href="/categories/">
        <span class="site-state-item-count">42</span>
        <span class="site-state-item-name">分类</span></a>
      </div>
      <div class="site-state-item site-state-tags">
        <span class="site-state-item-count">43</span>
        <span class="site-state-item-name">标签</span>
      </div>
  </nav>
</div>
  <div class="links-of-author animated">
      <span class="links-of-author-item">
        <a href="https://github.com/sumumm" title="GitHub → https:&#x2F;&#x2F;github.com&#x2F;sumumm" rel="noopener me" target="_blank"><i class="fab fa-github fa-fw"></i>GitHub</a>
      </span>
  </div>

        </div>
      </div>
    </div>

    
  </aside>


    </div>

    <div class="main-inner post posts-expand">


  


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="https://sumumm.github.io/post/e99b9cbc.html">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.jpg">
      <meta itemprop="name" content="苏木">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="苏木">
      <meta itemprop="description" content="莫道桑榆晚，为霞尚满天">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="LV05-01-uboot-04-uboot启动流程 | 苏木">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h1 class="post-title" itemprop="name headline">
          LV05-01-uboot-04-uboot启动流程
        </h1>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2023-09-08 19:22:56" itemprop="dateCreated datePublished" datetime="2023-09-08T19:22:56+08:00">2023-09-08</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/" itemprop="url" rel="index"><span itemprop="name">嵌入式开发</span></a>
        </span>
          ，
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/" itemprop="url" rel="index"><span itemprop="name">02IMX6ULL平台</span></a>
        </span>
          ，
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV05-uboot%E4%B8%8E%E5%86%85%E6%A0%B8/" itemprop="url" rel="index"><span itemprop="name">LV05-uboot与内核</span></a>
        </span>
    </span>

  
    <span class="post-meta-break"></span>
    <span class="post-meta-item" title="本文字数">
      <span class="post-meta-item-icon">
        <i class="far fa-file-word"></i>
      </span>
      <span class="post-meta-item-text">本文字数：</span>
      <span>19k</span>
    </span>
    <span class="post-meta-item" title="阅读时长">
      <span class="post-meta-item-icon">
        <i class="far fa-clock"></i>
      </span>
      <span class="post-meta-item-text">阅读时长 &asymp;</span>
      <span>1:08</span>
    </span>
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody"><p>本文主要是uboot启动流程的相关笔记。若笔记中有错误或者不合适的地方，欢迎批评指正😃。</p>
<span id="more"></span>

<!-- Photo: https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV05-uboot%E4%B8%8E%E5%86%85%E6%A0%B8/LV05-01-uboot-04-uboot%E5%90%AF%E5%8A%A8%E6%B5%81%E7%A8%8B/img/ -->

<details class="folding-tag" blue><summary> 点击查看使用工具及版本 </summary>
              <div class='content'>
              <table>    <tr>        <td align="center" width=150px>Windows版本</td>        <td align="left">windows11</td>    </tr>    <tr>        <td align="center">Ubuntu版本</td>        <td align="left">Ubuntu16.04的64位版本</td>      </tr>    <tr>        <td align="center">VMware® Workstation 16 Pro</td>        <td align="left">16.2.3 build-19376536</td>      </tr>    <tr>        <td align="center">终端软件</td>        <td align="left">MobaXterm(Professional Edition v23.0 Build 5042 (license))</td>      </tr>    <tr>        <td align="center">Linux开发板</td>        <td align="left">正点原子 i.MX6ULL Linux 阿尔法开发板</td>      </tr>    <tr>        <td align="center">uboot</td>        <td align="left">NXP官方提供的uboot，NXP提供的版本为uboot-imx-rel_imx_4.1.15_2.1.0_ga(使用的uboot版本为U-Boot 2016.03)</td>      </tr>    <tr>        <td align="center">linux内核</td>        <td align="left">linux-4.15(NXP官方提供)</td>      </tr>    <tr>        <td align="center">Win32DiskImager</td>        <td align="left">Win32DiskImager v1.0</td>      </tr></table>
              </div>
            </details>

<details class="folding-tag" blue><summary> 点击查看本文参考资料 </summary>
              <div class='content'>
              <table>    <tr>        <td align="center">分类</td>        <td align="center">网址</td>        <td align="center">说明</td>    </tr>    <tr>        <td align="center" rowspan="5">官方网站</td>        <td align="left"><a href="https://www.arm.com/" target="_blank">https://www.arm.com/</a></td>        <td align="left">ARM官方网站，在这里我们可以找到Cotex-Mx以及ARMVx的一些文档</td>    </tr>    <tr>        <td align="left"><a href="https://www.nxp.com.cn/" target="_blank">https://www.nxp.com.cn/ </a></td>        <td align="left">NXP官方网站</td>    </tr>    <tr>        <td align="left"><a href="https://www.nxpic.org.cn/" target="_blank">https://www.nxpic.org.cn/</a></td><td align="left">NXP 官方社区</td>    </tr>    <tr>        <td align="left"><a href="https://u-boot.readthedocs.io/en/latest/" target="_blank">https://u-boot.readthedocs.io/en/latest/</a></td><td align="left">u-boot官网</td>    </tr>    <tr>        <td align="left"><a href="https://www.kernel.org/" target="_blank">https://www.kernel.org/</a></td><td align="left">linux内核官网</td>    </tr></table>
              </div>
            </details>

<details class="folding-tag" blue><summary> 点击查看相关文件下载 </summary>
              <div class='content'>
              <table>    <tr>        <td align="center">分类</td>        <td align="center">网址</td>        <td align="center">说明</td>    </tr>    <tr>        <td align="center" rowspan="4">NXP</td>        <td align="left"><a href="https://github.com/nxp-imx" target="_blank">https://github.com/nxp-imx</a></td>        <td align="left">NXP imx开发资源GitHub组织，里边会有u-boot和linux内核的仓库</td>    </tr>    <tr>        <td align="left"><a href="https://elixir.bootlin.com/linux/latest/source" target="_blank">https://elixir.bootlin.com/linux/latest/source</a></td>        <td align="left">在线阅读linux kernel源码</td>    </tr>    <tr>        <td align="left"><a href="https://github.com/nxp-imx/linux-imx/releases/tag/rel_imx_4.1.15_2.1.0_ga" target="_blank">nxp-imx/linux-imx/releases/tag/rel_imx_4.1.15_2.1.0_ga</a></td>        <td align="left">NXP linux内核仓库tags中的rel_imx_4.1.15_2.1.0_ga</td>    </tr>    <tr>        <td align="left"><a href="https://github.com/nxp-imx/uboot-imx/releases/tag/rel_imx_4.1.15_2.1.0_ga" target="_blank">nxp-imx/uboot-imx/releases/tag/rel_imx_4.1.15_2.1.0_ga</a></td>        <td align="left">NXP u-boot仓库tags中的rel_imx_4.1.15_2.1.0_ga</td>    </tr>    <tr>        <td align="center" rowspan="2">I.MX6ULL</td>        <td align="left"><a href="https://www.nxp.com.cn/docs/en/data-sheet/IMX6ULLIEC.pdf" target="_blank">i.MX 6ULL Applications Processors for Industrial Products</a></td>        <td align="left">I.MX6ULL 芯片手册（datasheet，可以在线查看）</td>    </tr>    <tr>        <td align="left"><a href="https://www.nxp.com.cn/webapp/Download?colCode=IMX6ULLRM&lang_cd=zh" target="_blank">i.MX 6ULL Applications ProcessorReference Manual</a></td>        <td align="left">I.MX6ULL 参考手册（下载后才能查看，需要登录NXP官网）</td>    </tr></table>
              </div>
            </details>

<h1 id="一、链接脚本u-boot-lds"><a href="#一、链接脚本u-boot-lds" class="headerlink" title="一、链接脚本u-boot.lds  "></a><font size=3>一、链接脚本u-boot.lds  </font></h1><h2 id="1-程序入口"><a href="#1-程序入口" class="headerlink" title="1. 程序入口"></a><font size=3>1. 程序入口</font></h2><p>我们要分析uboot的启动流程，就要找到整个程序的“入口”，找到第一行程序在哪里。程序的链接是由链接脚本来决定的，所以通过链接脚本可以找到程序的入口。如果没有编译过 uboot 的话，链接脚本为uboot源码目录中的这个文件：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">arch/arm/cpu/u-boot.lds</span><br></pre></td></tr></table></figure>

<p>但是这个不是最终使用的链接脚本，最终的链接脚本是在这个链接脚本的基础上生成的。编译一下 uboot，编译完成以后就会在 uboot 根目录下生成 u-boot.lds文件：</p>
<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV05-uboot%E4%B8%8E%E5%86%85%E6%A0%B8/LV05-01-uboot-04-uboot%E5%90%AF%E5%8A%A8%E6%B5%81%E7%A8%8B/img/image-20221015091825040.png" alt="image-20221015091825040" style="zoom:50%;" />

<p><strong>【注意】</strong>只有编译 u-boot 以后才会在根目录下出现 u-boot.lds 文件 。</p>
<p>我们打开这个文件，会看到如下内容：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">OUTPUT_FORMAT(<span class="string">&quot;elf32-littlearm&quot;</span>, <span class="string">&quot;elf32-littlearm&quot;</span>, <span class="string">&quot;elf32-littlearm&quot;</span>)</span><br><span class="line">OUTPUT_ARCH(arm)</span><br><span class="line">ENTRY(_start)</span><br><span class="line">SECTIONS</span><br><span class="line">&#123;</span><br><span class="line"> . = <span class="number">0x00000000</span>;</span><br><span class="line"> . = ALIGN(<span class="number">4</span>);</span><br><span class="line"> .text :</span><br><span class="line"> &#123;</span><br><span class="line">  *(.__image_copy_start)</span><br><span class="line">  *(.vectors)</span><br><span class="line">  arch/arm/cpu/armv7/start.o (.text*)</span><br><span class="line">  *(.text*)</span><br><span class="line"> &#125;</span><br><span class="line"><span class="comment">// 后边的省略 ... ...</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>其中第 3 行为代码当前入口点： _start，关于 _start 在uboot源码中的这个文件中有定义：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">arch/arm/lib/vectors.S</span><br></pre></td></tr></table></figure>

<p>我们打开着这个文件，有如下内容：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line">.globl _start</span><br><span class="line"></span><br><span class="line">/* 中间注释省略 ... ... */</span><br><span class="line"></span><br><span class="line">	.section &quot;.vectors&quot;, &quot;ax&quot;</span><br><span class="line"></span><br><span class="line">/* 中间注释省略 ... ... */</span><br><span class="line"></span><br><span class="line">_start:</span><br><span class="line"></span><br><span class="line">#ifdef CONFIG_SYS_DV_NOR_BOOT_CFG</span><br><span class="line">	.word	CONFIG_SYS_DV_NOR_BOOT_CFG</span><br><span class="line">#endif</span><br><span class="line"></span><br><span class="line">	b	reset</span><br><span class="line">	ldr	pc, _undefined_instruction</span><br><span class="line">	ldr	pc, _software_interrupt</span><br><span class="line">	ldr	pc, _prefetch_abort</span><br><span class="line">	ldr	pc, _data_abort</span><br><span class="line">	ldr	pc, _not_used</span><br><span class="line">	ldr	pc, _irq</span><br><span class="line">	ldr	pc, _fiq</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>可以看出， _start 后面就是中断向量表，从图中的“ .section “ .vectors “，” ax ” 可以得到，此<strong>代码存放在 .vectors 段里面</strong>。</p>
<h2 id="2-uboot起始地址"><a href="#2-uboot起始地址" class="headerlink" title="2. uboot起始地址"></a><font size=3>2. uboot起始地址</font></h2><p>我们使用以下命令在 uboot 源码中查找 __image_copy_start ：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">grep -nR &quot;__image_copy_start&quot;</span><br></pre></td></tr></table></figure>

<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV05-uboot%E4%B8%8E%E5%86%85%E6%A0%B8/LV05-01-uboot-04-uboot%E5%90%AF%E5%8A%A8%E6%B5%81%E7%A8%8B/img/image-20221015092906081.png" alt="image-20221015092906081" style="zoom:50%;" />

<p>我们打开 u-boot.map  文件，搜索 __image_copy_start 可以找到如下内容：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 上边部分省略 ... ...</span></span><br><span class="line">链结器命令稿和内存映射</span><br><span class="line"></span><br><span class="line">段 .text 的地址设置为 <span class="number">0x87800000</span></span><br><span class="line">                <span class="number">0x0000000000000000</span>                . = <span class="number">0x0</span></span><br><span class="line">                <span class="number">0x0000000000000000</span>                . = ALIGN (<span class="number">0x4</span>)</span><br><span class="line"></span><br><span class="line">.text           <span class="number">0x0000000087800000</span>    <span class="number">0x3e734</span></span><br><span class="line"> *(.__image_copy_start)</span><br><span class="line"> .__image_copy_start</span><br><span class="line">                <span class="number">0x0000000087800000</span>        <span class="number">0x0</span> arch/arm/lib/built-in.o</span><br><span class="line">                <span class="number">0x0000000087800000</span>                __image_copy_start</span><br><span class="line"> *(.vectors)</span><br><span class="line"> .vectors       <span class="number">0x0000000087800000</span>      <span class="number">0x300</span> arch/arm/lib/built-in.o</span><br><span class="line">                <span class="number">0x0000000087800000</span>                _start</span><br><span class="line">                <span class="number">0x0000000087800020</span>                _undefined_instruction</span><br><span class="line">                <span class="number">0x0000000087800024</span>                _software_interrupt</span><br><span class="line">                <span class="number">0x0000000087800028</span>                _prefetch_abort</span><br><span class="line">                <span class="number">0x000000008780002c</span>                _data_abort</span><br><span class="line">                <span class="number">0x0000000087800030</span>                _not_used</span><br><span class="line">                <span class="number">0x0000000087800034</span>                _irq</span><br><span class="line">                <span class="number">0x0000000087800038</span>                _fiq</span><br><span class="line">                <span class="number">0x0000000087800040</span>                IRQ_STACK_START_IN</span><br><span class="line"><span class="comment">// 下边部分省略 ... ...</span></span><br></pre></td></tr></table></figure>

<p>u-boot.map 是 uboot 的映射文件，编译 uboot 后存在于源码顶层目录中，可以从此文件看到某个文件或者函数链接到了哪个地址。可以看到 __image_copy_start 为 0X87800000，而 .text 的起始地址也是 0X87800000 。  </p>
<p>我们再打开uboot源码目录中的链接文件 u-boot.lds ，可以看到有如下语句：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">OUTPUT_FORMAT(<span class="string">&quot;elf32-littlearm&quot;</span>, <span class="string">&quot;elf32-littlearm&quot;</span>, <span class="string">&quot;elf32-littlearm&quot;</span>)</span><br><span class="line">OUTPUT_ARCH(arm)</span><br><span class="line">ENTRY(_start)</span><br><span class="line">SECTIONS</span><br><span class="line">&#123;</span><br><span class="line"> . = <span class="number">0x00000000</span>;</span><br><span class="line"> . = ALIGN(<span class="number">4</span>);</span><br><span class="line"> .text :</span><br><span class="line"> &#123;</span><br><span class="line">  *(.__image_copy_start)</span><br><span class="line">  *(.vectors)</span><br><span class="line">  arch/arm/cpu/armv7/start.o (.text*)</span><br><span class="line">  *(.text*)</span><br><span class="line"> &#125;</span><br><span class="line"><span class="comment">// 后边的省略 ... ...</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>可以看到第 11 行是 vectors 段， vectors 段保存中断向量表 ，我们从 arch&#x2F;arm&#x2F;lib&#x2F;vectors.S 文件中知道 vectors.S 的代码是存在 vectors 段中的。  vectors 段的起始地址也是 0X87800000，说明<strong>整个 uboot 的起始地址就是 0X87800000</strong>，这也是为什么我们裸机可执行程序的链接起始地址选择 0X87800000 了，目的就是为了和 uboot 一致。  </p>
<p>u-boot.lds 的第12行是将 arch&#x2F;arm&#x2F;cpu&#x2F;armv7&#x2F;start.s 编译出来的代码放到中断向量表后面。  </p>
<p>u-boot.lds 的第13行是 text 段，其他的代码段就放到这里 。</p>
<h2 id="3-与地址相关的变量"><a href="#3-与地址相关的变量" class="headerlink" title="3. 与地址相关的变量"></a><font size=3>3. 与地址相关的变量</font></h2><p> 在 u-boot.lds 中有一些跟地址有关的“变量”需要我们注意一下，这些变量要最终编译完成才能确定的：</p>
<table>
<thead>
<tr>
<th>变量</th>
<th>数值</th>
<th>描述</th>
</tr>
</thead>
<tbody><tr>
<td>__image_copy_start</td>
<td>0x87800000</td>
<td>uboot 拷贝的首地址</td>
</tr>
<tr>
<td>__image_copy_end</td>
<td>0x8785dd54</td>
<td>uboot 拷贝的结束地址</td>
</tr>
<tr>
<td>__rel_dyn_start</td>
<td>0x8785dd54</td>
<td>.rel.dyn 段起始地址</td>
</tr>
<tr>
<td>__rel_dyn_end</td>
<td>0x878668f4</td>
<td>.rel.dyn 段结束地址</td>
</tr>
<tr>
<td>_image_binary_end</td>
<td>0x878668f4</td>
<td>镜像结束地址</td>
</tr>
<tr>
<td>__bss_start</td>
<td>0x8785dd54</td>
<td>.bss 段起始地址</td>
</tr>
<tr>
<td>__bss_end</td>
<td>0x878a8e74</td>
<td>.bss 段结束地址</td>
</tr>
</tbody></table>
<p align="center">表一-3-1</p>

<p>这些“变量”值可以在 u-boot.map 文件中查找，上表中除了__image_copy_start以外，其他的变量值每次编译的时候可能会变化，如果修改了 uboot 代码、修改了 uboot 配置、选用不同的优化等级等等都会影响到这些值。所以，一切以实际值为准。</p>
<h1 id="二、uboot启动流程"><a href="#二、uboot启动流程" class="headerlink" title="二、uboot启动流程"></a><font size=3>二、uboot启动流程</font></h1><h2 id="1-reset-函数"><a href="#1-reset-函数" class="headerlink" title="1. reset 函数"></a><font size=3>1. reset 函数</font></h2><h3 id="1-1-reset-函数在哪？"><a href="#1-1-reset-函数在哪？" class="headerlink" title="1.1 reset 函数在哪？"></a><font size=3>1.1 reset 函数在哪？</font></h3><p>从 u-boot.lds 中我们已经知道了入口点是 arch&#x2F;arm&#x2F;lib&#x2F;vectors.S 文件中的_start，相关代码如下：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line">.globl _start</span><br><span class="line"></span><br><span class="line">/* 中间注释省略 ... ... */</span><br><span class="line"></span><br><span class="line">	.section &quot;.vectors&quot;, &quot;ax&quot;</span><br><span class="line"></span><br><span class="line">/* 中间注释省略 ... ... */</span><br><span class="line"></span><br><span class="line">_start:</span><br><span class="line"></span><br><span class="line">#ifdef CONFIG_SYS_DV_NOR_BOOT_CFG</span><br><span class="line">	.word	CONFIG_SYS_DV_NOR_BOOT_CFG</span><br><span class="line">#endif</span><br><span class="line"></span><br><span class="line">	b	reset</span><br><span class="line">	ldr	pc, _undefined_instruction</span><br><span class="line">	ldr	pc, _software_interrupt</span><br><span class="line">	ldr	pc, _prefetch_abort</span><br><span class="line">	ldr	pc, _data_abort</span><br><span class="line">	ldr	pc, _not_used</span><br><span class="line">	ldr	pc, _irq</span><br><span class="line">	ldr	pc, _fiq</span><br></pre></td></tr></table></figure>

<p>_start后边为中断向量表，程序启动后，会首先跳转到 reset 函数里边，而 reset 函数则是定义在uboot源码的这个文件中：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">arch/arm/cpu/armv7/start.S</span><br></pre></td></tr></table></figure>

<p>我们打开这个文件，找到reset，会有如下内容：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">reset:</span><br><span class="line">	/* Allow the board to save important registers */</span><br><span class="line">	b	save_boot_params</span><br></pre></td></tr></table></figure>

<p>可以看到 reset 中又从 reset 函数跳转到了 save_boot_params 函数，而 save_boot_params 函数同样定义在 start.S 里面 ：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">/*************************************************************************</span><br><span class="line"> *</span><br><span class="line"> * void save_boot_params(u32 r0, u32 r1, u32 r2, u32 r3)</span><br><span class="line"> *	__attribute__((weak));</span><br><span class="line"> *</span><br><span class="line"> * Stack pointer is not yet initialized at this moment</span><br><span class="line"> * Don&#x27;t save anything to stack even if compiled with -O0</span><br><span class="line"> *</span><br><span class="line"> *************************************************************************/</span><br><span class="line">ENTRY(save_boot_params)</span><br><span class="line">	b	save_boot_params_ret		@ back to my caller</span><br><span class="line">ENDPROC(save_boot_params)</span><br><span class="line">	.weak	save_boot_params</span><br></pre></td></tr></table></figure>

<p>发现 save_boot_params 函数也是只有一句跳转语句，跳转到 save_boot_params_ret 函数，save_boot_params_ret 也是定义在 start.S 文件中：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">save_boot_params_ret:</span><br><span class="line">	<span class="comment">/*</span></span><br><span class="line"><span class="comment">	 * disable interrupts (FIQ and IRQ), also set the cpu to SVC32 mode,</span></span><br><span class="line"><span class="comment">	 * except if in HYP mode already</span></span><br><span class="line"><span class="comment">	 */</span></span><br><span class="line">	mrs	r0, cpsr</span><br><span class="line">	and	r1, r0, #<span class="number">0x1f</span>		@ mask mode bits</span><br><span class="line">	teq	r1, #<span class="number">0x1a</span>		@ test <span class="keyword">for</span> HYP mode</span><br><span class="line">	bicne	r0, r0, #<span class="number">0x1f</span>		@ clear all mode bits</span><br><span class="line">	orrne	r0, r0, #<span class="number">0x13</span>		@ <span class="built_in">set</span> SVC mode</span><br><span class="line">	orr	r0, r0, #<span class="number">0xc0</span>		@ disable FIQ and IRQ</span><br><span class="line">	msr	cpsr,r0</span><br><span class="line"><span class="comment">/* 后边的省略 ... ... */</span></span><br></pre></td></tr></table></figure>

<h3 id="1-2-最终做了什么？"><a href="#1-2-最终做了什么？" class="headerlink" title="1.2 最终做了什么？"></a><font size=3>1.2 最终做了什么？</font></h3><h4 id="1-2-1-设置工作状态"><a href="#1-2-1-设置工作状态" class="headerlink" title="1.2.1 设置工作状态"></a><font size=3>1.2.1 设置工作状态</font></h4><p>上边我们通过reset找到了 save_boot_params_ret 函数，reset函数经过多次跳转，最终执行的是 save_boot_params_ret 函数中的内容，如下所示：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">save_boot_params_ret:</span><br><span class="line">	/*</span><br><span class="line">	 * disable interrupts (FIQ and IRQ), also set the cpu to SVC32 mode,</span><br><span class="line">	 * except if in HYP mode already</span><br><span class="line">	 */</span><br><span class="line">	mrs	r0, cpsr</span><br><span class="line">	and	r1, r0, #0x1f		@ mask mode bits</span><br><span class="line">	teq	r1, #0x1a		@ test for HYP mode</span><br><span class="line">	bicne	r0, r0, #0x1f		@ clear all mode bits</span><br><span class="line">	orrne	r0, r0, #0x13		@ set SVC mode</span><br><span class="line">	orr	r0, r0, #0xc0		@ disable FIQ and IRQ</span><br><span class="line">	msr	cpsr,r0</span><br><span class="line">/* 后边的省略 ... ... */</span><br></pre></td></tr></table></figure>

<p>第 6 行：读取寄存器 cpsr 中的值，并保存到 r0 寄存器中。  </p>
<p>第 7 行：将寄存器 r0 中的值与 0X1F 进行与运算，结果保存到 r1 寄存器中，目的就是提取 cpsr 寄存器的 bit[4:0] 这 5 位，这 5 位表示M4 M3 M2 M1 M0，而 M[4:0] 这五位用来设置处理器的工作模式。</p>
<details class="folding-tag" blue><summary> 点击查看 Cotex-A7 工作模式 </summary>
              <div class='content'>
              <table><thead><tr><th align="center">M[4:0]</th><th align="center">模式</th></tr></thead><tbody><tr><td align="center">10000</td><td align="center">User(usr)</td></tr><tr><td align="center">10001</td><td align="center">FIQ(fiq)</td></tr><tr><td align="center">10010</td><td align="center">IRQ(irq)</td></tr><tr><td align="center">10011</td><td align="center">Supervisor(svc)</td></tr><tr><td align="center">10110</td><td align="center">Monitor(mon)</td></tr><tr><td align="center">10111</td><td align="center">Abort(abt)</td></tr><tr><td align="center">11010</td><td align="center">Hyp(hyp)</td></tr><tr><td align="center">11011</td><td align="center">Undefined(und)</td></tr><tr><td align="center">11111</td><td align="center">System(sys)</td></tr></tbody></table>
              </div>
            </details>  

<p>第 8 行：判断 r1 寄存器的值是否等于 0X1A(0b11010)，也就是判断当前处理器模式是否处于 Hyp 模式。<br>第 9 行：如果 r1 和 0X1A 不相等，也就是 CPU 不处于 Hyp 模式的话就将 r0 寄存器的 bit[4:0] 进行清零，其实就是清除模式位<br>第 10 行：如果处理器不处于 Hyp 模式的话就将 r0 的寄存器的值与 0x13 进行或运算，0x13&#x3D;0b10011，也就是设置处理器进入 SVC 模式。<br>第 11 行， r0 寄存器的值再与 0xC0 进行或运算，那么 r0 寄存器此时的值就是 0xD3， cpsr 寄存器的 I 位和 F 位分别控制 IRQ 和 FIQ 两个中断的开关，设置为 1 就关闭了 FIQ 和 IRQ。<br>第 12 行，将 r0 寄存器写回到 cpsr 寄存器中。完成设置 CPU 处于 SVC32 模式，并且关闭 FIQ 和 IRQ 这两个中断。 </p>
<h4 id="1-2-2-设置向量表"><a href="#1-2-2-设置向量表" class="headerlink" title="1.2.2 设置向量表"></a><font size=3>1.2.2 设置向量表</font></h4><p>接下来，还是 save_boot_params_ret 函数，会继续执行以下代码：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">/*</span><br><span class="line"> * Setup vector:</span><br><span class="line"> * (OMAP4 spl TEXT_BASE is not 32 byte aligned.</span><br><span class="line"> * Continue to use ROM code vector only in OMAP4 spl)</span><br><span class="line"> */</span><br><span class="line">#if !(defined(CONFIG_OMAP44XX) &amp;&amp; defined(CONFIG_SPL_BUILD))</span><br><span class="line">	/* Set V=0 in CP15 SCTLR register - for VBAR to point to vector */</span><br><span class="line">	mrc	p15, 0, r0, c1, c0, 0	@ Read CP15 SCTLR Register</span><br><span class="line">	bic	r0, #CR_V		@ V = 0</span><br><span class="line">	mcr	p15, 0, r0, c1, c0, 0	@ Write CP15 SCTLR Register</span><br><span class="line"></span><br><span class="line">	/* Set vector address in CP15 VBAR register */</span><br><span class="line">	ldr	r0, =_start</span><br><span class="line">	mcr	p15, 0, r0, c12, c0, 0	@Set VBAR</span><br><span class="line">#endif</span><br></pre></td></tr></table></figure>

<p>第 6 行：如果没有定义 CONFIG_OMAP44XX 和 CONFIG_SPL_BUILD 的话条件成立，此处条件成立。  </p>
<p>第 8 行：读取 CP15 中 c1 寄存器的值到 r0 寄存器中，根据协处理器相关知识，这里是读取 SCTLR 寄存器的值。</p>
<p>第 9 行：CR_V 在 arch&#x2F;arm&#x2F;include&#x2F;asm&#x2F;system.h 中有如下所示定义：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">define</span> CR_V	(1 &lt;&lt; 13)	<span class="comment">/* Vectors relocated to 0xffff0000	*/</span></span></span><br></pre></td></tr></table></figure>

<p>因此这一行的目的就是清除 SCTLR 寄存器中的 bit[13]。bit[13] 为 V 位，此位是向量表控制位，当为 0 的时候向量表基地址为  0X00000000 ，软件可以重定位向量表。为 1 的时候向量表基地址为 0XFFFF0000，软件不能重定位向量表。这里将 V 清零，目的就是为了接下来的向量表重定位。</p>
<p>第 10 行：将 r0 寄存器的值重新写入到寄存器 SCTLR 中。  </p>
<p>第 13 行：设置 r0 寄存器的值为 _start， _start就是整个 uboot 的入口地址，其值为0X87800000，相当于 uboot 的起始地址，因此 0x87800000 也是向量表的起始地址。</p>
<p>第 14 行：将 r0 寄存器的值（向量表值）写入到 CP15 的 c12 寄存器中，也就是 VBAR 寄存器。因此第 8~14 行就是设置向量表重定位的。  </p>
<h4 id="1-2-3-最终跳转到-main"><a href="#1-2-3-最终跳转到-main" class="headerlink" title="1.2.3 最终跳转到 main"></a><font size=3>1.2.3 最终跳转到 main</font></h4><p>接下来就是：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">	/* the mask ROM code should have PLL and others stable */</span><br><span class="line">#ifndef CONFIG_SKIP_LOWLEVEL_INIT</span><br><span class="line">	bl	cpu_init_cp15</span><br><span class="line">	bl	cpu_init_crit</span><br><span class="line">#endif</span><br><span class="line"></span><br><span class="line">	bl	_main</span><br></pre></td></tr></table></figure>

<p>第 2 行：如果没有定义 CONFIG_SKIP_LOWLEVEL_INIT 的话条件成立。我们没有定义: CONFIG_SKIP_LOWLEVEL_INIT，因此条件成立，执行下面的语句。  </p>
<p>第 3 行：调用 cpu_init_cp15 函数，用来设置 CP15 相关的内容，比如关闭 MMU 等，此函数同样在 start.S文件中定义的，这里就暂时不关心这个函数了。</p>
<p>第 4 行：调用 cpu_init_crit函数，而该函数内部仅仅是调用了函数 lowlevel_init （这个函数后边会再分析）。</p>
<p>第 7 行：跳转到 main 函数。</p>
<h2 id="2-lowlevel-init-函数"><a href="#2-lowlevel-init-函数" class="headerlink" title="2. lowlevel_init 函数"></a><font size=3>2. lowlevel_init 函数</font></h2><p>上边我们分析过，到执行 main 函数之前，还会调用lowlevel_init  函数，接下来我们看一下这个函数是干嘛的。</p>
<h3 id="2-1-lowlevel-init-函数在哪？"><a href="#2-1-lowlevel-init-函数在哪？" class="headerlink" title="2.1 lowlevel_init 函数在哪？"></a><font size=3>2.1 lowlevel_init 函数在哪？</font></h3><p>lowlevel_init 这个函数在uboot源码的这个文件中定义 ：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">arch/arm/cpu/armv7/lowlevel_init.S</span><br></pre></td></tr></table></figure>

<p>我们打开这个文件会看到有如下内容：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br></pre></td><td class="code"><pre><span class="line">/*</span><br><span class="line"> * A lowlevel_init function that sets up the stack to call a C function to</span><br><span class="line"> * perform further init.</span><br><span class="line"> *</span><br><span class="line"> * (C) Copyright 2010</span><br><span class="line"> * Texas Instruments, &lt;www.ti.com&gt;</span><br><span class="line"> *</span><br><span class="line"> * Author :</span><br><span class="line"> *	Aneesh V	&lt;aneesh@ti.com&gt;</span><br><span class="line"> *</span><br><span class="line"> * SPDX-License-Identifier:	GPL-2.0+</span><br><span class="line"> */</span><br><span class="line"></span><br><span class="line">#include &lt;asm-offsets.h&gt;</span><br><span class="line">#include &lt;config.h&gt;</span><br><span class="line">#include &lt;linux/linkage.h&gt;</span><br><span class="line"></span><br><span class="line">ENTRY(lowlevel_init)</span><br><span class="line">	/*</span><br><span class="line">	 * Setup a temporary stack. Global data is not available yet.</span><br><span class="line">	 */</span><br><span class="line">	ldr	sp, =CONFIG_SYS_INIT_SP_ADDR</span><br><span class="line">	bic	sp, sp, #7 /* 8-byte alignment for ABI compliance */</span><br><span class="line">#ifdef CONFIG_SPL_DM</span><br><span class="line">	mov	r9, #0</span><br><span class="line">#else</span><br><span class="line">	/*</span><br><span class="line">	 * Set up global data for boards that still need it. This will be</span><br><span class="line">	 * removed soon.</span><br><span class="line">	 */</span><br><span class="line">#ifdef CONFIG_SPL_BUILD</span><br><span class="line">	ldr	r9, =gdata</span><br><span class="line">#else</span><br><span class="line">	sub	sp, sp, #GD_SIZE</span><br><span class="line">	bic	sp, sp, #7</span><br><span class="line">	mov	r9, sp</span><br><span class="line">#endif</span><br><span class="line">#endif</span><br><span class="line">	/*</span><br><span class="line">	 * Save the old lr(passed in ip) and the current lr to stack</span><br><span class="line">	 */</span><br><span class="line">	push	&#123;ip, lr&#125;</span><br><span class="line"></span><br><span class="line">	/*</span><br><span class="line">	 * Call the very early init function. This should do only the</span><br><span class="line">	 * absolute bare minimum to get started. It should not:</span><br><span class="line">	 *</span><br><span class="line">	 * - set up DRAM</span><br><span class="line">	 * - use global_data</span><br><span class="line">	 * - clear BSS</span><br><span class="line">	 * - try to start a console</span><br><span class="line">	 *</span><br><span class="line">	 * For boards with SPL this should be empty since SPL can do all of</span><br><span class="line">	 * this init in the SPL board_init_f() function which is called</span><br><span class="line">	 * immediately after this.</span><br><span class="line">	 */</span><br><span class="line">	bl	s_init</span><br><span class="line">	pop	&#123;ip, pc&#125;</span><br><span class="line">ENDPROC(lowlevel_init)</span><br></pre></td></tr></table></figure>

<h3 id="2-2-最终做了什么？"><a href="#2-2-最终做了什么？" class="headerlink" title="2.2 最终做了什么？"></a><font size=3>2.2 最终做了什么？</font></h3><h4 id="2-1-设置堆栈指针-sp"><a href="#2-1-设置堆栈指针-sp" class="headerlink" title="2.1 设置堆栈指针 sp"></a><font size=3>2.1 设置堆栈指针 sp</font></h4><p>第 22 行代码内容如下：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ldr	sp, =CONFIG_SYS_INIT_SP_ADDR</span><br></pre></td></tr></table></figure>

<p>该语句设置 sp 指向 CONFIG_SYS_INIT_SP_ADDR， CONFIG_SYS_INIT_SP_ADDR 在 include&#x2F;configs&#x2F;mx6ullevk.h 文件中有如下所示定义：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">define</span> CONFIG_SYS_INIT_RAM_ADDR	IRAM_BASE_ADDR</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> CONFIG_SYS_INIT_RAM_SIZE	IRAM_SIZE</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> CONFIG_SYS_INIT_SP_OFFSET \</span></span><br><span class="line"><span class="meta">	(CONFIG_SYS_INIT_RAM_SIZE - GENERATED_GBL_DATA_SIZE)</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> CONFIG_SYS_INIT_SP_ADDR \</span></span><br><span class="line"><span class="meta">	(CONFIG_SYS_INIT_RAM_ADDR + CONFIG_SYS_INIT_SP_OFFSET)</span></span><br></pre></td></tr></table></figure>

<details class="folding-tag" blue><summary> 点击查看 IRAM_BASE_ADDR 和 IRAM_SIZE 的值 </summary>
              <div class='content'>
              <p>IRAM_BASE_ADDR 和 IRAM_SIZE 在 文 件 arch&#x2F;arm&#x2F;include&#x2F;asm&#x2F;arch-mx6&#x2F;imx-regs.h 中有定义，如下所示，其实就是IMX6UL&#x2F;IM6ULL 内部 ocram 的首地址和大小。 </p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">define</span> IRAM_BASE_ADDR			0x00900000</span></span><br><span class="line"><span class="comment">// 它俩并不挨着，中间的省略 ... ...</span></span><br><span class="line"><span class="meta">#<span class="keyword">if</span> !(defined(CONFIG_MX6SX) || defined(CONFIG_MX6UL) || \</span></span><br><span class="line"><span class="meta">	defined(CONFIG_MX6SLL) || defined(CONFIG_MX6SL))</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> IRAM_SIZE                    0x00040000</span></span><br><span class="line"><span class="meta">#<span class="keyword">else</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> IRAM_SIZE                    0x00020000</span></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br></pre></td></tr></table></figure><p> 如果第 3 行的条件成立的话 IRAM_SIZE&#x3D;0X40000，当定义了 CONFIG_MX6SX、CONFIG_MX6U、 CONFIG_MX6SLL 和CONFIG_MX6SL 中的任意一个的话，条件就不成立，在 .config 中定义了 CONFIG_MX6UL，所以条件不成立，所以最终IRAM_SIZE &#x3D; 0X20000 &#x3D; 128KB。所以我们可以得到如下值：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">CONFIG_SYS_INIT_RAM_ADDR = IRAM_BASE_ADDR = <span class="number">0x00900000</span></span><br><span class="line">CONFIG_SYS_INIT_RAM_SIZE = <span class="number">0x00020000</span> = <span class="number">128</span>KB</span><br></pre></td></tr></table></figure>
              </div>
            </details>

<details class="folding-tag" blue><summary> 点击查看 GENERATED_GBL_DATA_SIZE 的值 </summary>
              <div class='content'>
              <p>GENERATED_GBL_DATA_SIZE 在文件 include&#x2F;generated&#x2F;generic-asm-offsets.h 中有定义：  </p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">ifndef</span> __GENERIC_ASM_OFFSETS_H__</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> __GENERIC_ASM_OFFSETS_H__</span></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment"> * DO NOT MODIFY.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * This file was generated by Kbuild</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> GENERATED_GBL_DATA_SIZE 256 <span class="comment">/* (sizeof(struct global_data) + 15) &amp; ~15	@ */</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> GENERATED_BD_INFO_SIZE 80 <span class="comment">/* (sizeof(struct bd_info) + 15) &amp; ~15	@ */</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> GD_SIZE 248 <span class="comment">/* sizeof(struct global_data)	@ */</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> GD_BD 0 <span class="comment">/* offsetof(struct global_data, bd)	@ */</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> GD_MALLOC_BASE 192 <span class="comment">/* offsetof(struct global_data, malloc_base)	@ */</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> GD_RELOCADDR 48 <span class="comment">/* offsetof(struct global_data, relocaddr)	@ */</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> GD_RELOC_OFF 68 <span class="comment">/* offsetof(struct global_data, reloc_off)	@ */</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> GD_START_ADDR_SP 64 <span class="comment">/* offsetof(struct global_data, start_addr_sp)	@ */</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br></pre></td></tr></table></figure><p>第 9 行：GENERATED_GBL_DATA_SIZE&#x3D;256， GENERATED_GBL_DATA_SIZE 的含义为 (sizeof(struct global_data) + 15) &amp; ~15 。所以我们可以得到如下值：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">GENERATED_GBL_DATA_SIZE = <span class="number">256</span></span><br></pre></td></tr></table></figure>
              </div>
            </details>

<p>所以，所需要的变量有如下三个：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">CONFIG_SYS_INIT_RAM_ADDR = IRAM_BASE_ADDR = <span class="number">0x00900000</span></span><br><span class="line">CONFIG_SYS_INIT_RAM_SIZE = <span class="number">0x00020000</span> = <span class="number">128</span>KB</span><br><span class="line">GENERATED_GBL_DATA_SIZE = <span class="number">256</span></span><br></pre></td></tr></table></figure>

<p>故，CONFIG_SYS_INIT_SP_ADDR  的值如下：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">CONFIG_SYS_INIT_SP_OFFSET = CONFIG_SYS_INIT_RAM_SIZE - GENERATED_GBL_DATA_SIZE = <span class="number">0x00020000</span> - <span class="number">256</span> = <span class="number">0x1FF00</span></span><br><span class="line">CONFIG_SYS_INIT_SP_ADDR = CONFIG_SYS_INIT_RAM_ADDR + CONFIG_SYS_INIT_SP_OFFSET = <span class="number">0x00900000</span> + <span class="number">0x1FF00</span> = <span class="number">0X0091FF00</span></span><br></pre></td></tr></table></figure>

<p>此时的 sp 指向 0X91FF00，这属于 IMX6UL&#x2F;IMX6ULL 的内部 ram。  </p>
<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV05-uboot%E4%B8%8E%E5%86%85%E6%A0%B8/LV05-01-uboot-04-uboot%E5%90%AF%E5%8A%A8%E6%B5%81%E7%A8%8B/img/image-20221015130030906.png" alt="image-20221015130030906" style="zoom: 43%;" />

<h4 id="2-2-sp指针8字节对齐"><a href="#2-2-sp指针8字节对齐" class="headerlink" title="2.2 sp指针8字节对齐"></a><font size=3>2.2 sp指针8字节对齐</font></h4><p>第 23 行代码：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">bic	sp, sp, #7 /* 8-byte alignment for ABI compliance */</span><br></pre></td></tr></table></figure>

<h4 id="2-3-sp指针其他操作"><a href="#2-3-sp指针其他操作" class="headerlink" title="2.3 sp指针其他操作"></a><font size=3>2.3 sp指针其他操作</font></h4><p>第 34 行：sp 指针减去 GD_SIZE， GD_SIZE 同样在 include&#x2F;generated&#x2F;generic-asm-offsets.h 中定了，大小为 248。</p>
<p>第 35 行：对 sp 做 8 字节对齐，此时 sp 的地址为 0X0091FF00 - 248 &#x3D; 0X0091FE08。此时的 sp 指针如下图所示：</p>
<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV05-uboot%E4%B8%8E%E5%86%85%E6%A0%B8/LV05-01-uboot-04-uboot%E5%90%AF%E5%8A%A8%E6%B5%81%E7%A8%8B/img/image-20221015130125594.png" alt="image-20221015130125594" style="zoom:40%;" />

<p>第 36 行：将 sp 地址保存在 r9 寄存器中。</p>
<p>第 42 行：将 ip 和 lr 压栈</p>
<p>第 57 行：调用函数 s_init，这个函数我们后边再说。</p>
<p>第 58 行：将第 36 行入栈的 ip 和 lr 进行出栈，并将 lr 赋给 pc。  </p>
<h2 id="3-s-init-函数"><a href="#3-s-init-函数" class="headerlink" title="3. s_init 函数"></a><font size=3>3. s_init 函数</font></h2><h3 id="3-1-s-init-函数在哪？"><a href="#3-1-s-init-函数在哪？" class="headerlink" title="3.1 s_init 函数在哪？"></a><font size=3>3.1 s_init 函数在哪？</font></h3><p>前边我们分析 lowlevel_init 函数的时候，发现这个函数在最后调用了 s_init 函数，s_init 函数定义在uboot源码的这个文件中：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">arch/arm/cpu/armv7/mx6/soc.c</span><br></pre></td></tr></table></figure>

<p>我们打开这个文件，会看到此函数定义如下：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">void</span> <span class="title function_">s_init</span><span class="params">(<span class="type">void</span>)</span></span><br><span class="line">&#123;</span><br><span class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">anatop_regs</span> *<span class="title">anatop</span> =</span> (<span class="keyword">struct</span> anatop_regs *)ANATOP_BASE_ADDR;</span><br><span class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">mxc_ccm_reg</span> *<span class="title">ccm</span> =</span> (<span class="keyword">struct</span> mxc_ccm_reg *)CCM_BASE_ADDR;</span><br><span class="line">	u32 mask480;</span><br><span class="line">	u32 mask528;</span><br><span class="line">	u32 reg, periph1, periph2;</span><br><span class="line"></span><br><span class="line">	<span class="keyword">if</span> (is_cpu_type(MXC_CPU_MX6SX) || is_cpu_type(MXC_CPU_MX6UL) ||</span><br><span class="line">	    is_cpu_type(MXC_CPU_MX6ULL) || is_cpu_type(MXC_CPU_MX6SLL))</span><br><span class="line">		<span class="keyword">return</span>;</span><br><span class="line"></span><br><span class="line">	<span class="comment">/* Due to hardware limitation, on MX6Q we need to gate/ungate all PFDs</span></span><br><span class="line"><span class="comment">	 * to make sure PFD is working right, otherwise, PFDs may</span></span><br><span class="line"><span class="comment">	 * not output clock after reset, MX6DL and MX6SL have added 396M pfd</span></span><br><span class="line"><span class="comment">	 * workaround in ROM code, as bus clock need it</span></span><br><span class="line"><span class="comment">	 */</span></span><br><span class="line"></span><br><span class="line">	mask480 = ANATOP_PFD_CLKGATE_MASK(<span class="number">0</span>) |</span><br><span class="line">		ANATOP_PFD_CLKGATE_MASK(<span class="number">1</span>) |</span><br><span class="line">		ANATOP_PFD_CLKGATE_MASK(<span class="number">2</span>) |</span><br><span class="line">		ANATOP_PFD_CLKGATE_MASK(<span class="number">3</span>);</span><br><span class="line">	mask528 = ANATOP_PFD_CLKGATE_MASK(<span class="number">1</span>) |</span><br><span class="line">		ANATOP_PFD_CLKGATE_MASK(<span class="number">3</span>);</span><br><span class="line"></span><br><span class="line">	reg = readl(&amp;ccm-&gt;cbcmr);</span><br><span class="line">	periph2 = ((reg &amp; MXC_CCM_CBCMR_PRE_PERIPH2_CLK_SEL_MASK)</span><br><span class="line">		&gt;&gt; MXC_CCM_CBCMR_PRE_PERIPH2_CLK_SEL_OFFSET);</span><br><span class="line">	periph1 = ((reg &amp; MXC_CCM_CBCMR_PRE_PERIPH_CLK_SEL_MASK)</span><br><span class="line">		&gt;&gt; MXC_CCM_CBCMR_PRE_PERIPH_CLK_SEL_OFFSET);</span><br><span class="line"></span><br><span class="line">	<span class="comment">/* Checking if PLL2 PFD0 or PLL2 PFD2 is using for periph clock */</span></span><br><span class="line">	<span class="keyword">if</span> ((periph2 != <span class="number">0x2</span>) &amp;&amp; (periph1 != <span class="number">0x2</span>))</span><br><span class="line">		mask528 |= ANATOP_PFD_CLKGATE_MASK(<span class="number">0</span>);</span><br><span class="line"></span><br><span class="line">	<span class="keyword">if</span> ((periph2 != <span class="number">0x1</span>) &amp;&amp; (periph1 != <span class="number">0x1</span>) &amp;&amp;</span><br><span class="line">		(periph2 != <span class="number">0x3</span>) &amp;&amp; (periph1 != <span class="number">0x3</span>))</span><br><span class="line">		mask528 |= ANATOP_PFD_CLKGATE_MASK(<span class="number">2</span>);</span><br><span class="line"></span><br><span class="line">	writel(mask480, &amp;anatop-&gt;pfd_480_set);</span><br><span class="line">	writel(mask528, &amp;anatop-&gt;pfd_528_set);</span><br><span class="line">	writel(mask480, &amp;anatop-&gt;pfd_480_clr);</span><br><span class="line">	writel(mask528, &amp;anatop-&gt;pfd_528_clr);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="3-2-最终做了什么？"><a href="#3-2-最终做了什么？" class="headerlink" title="3.2 最终做了什么？"></a><font size=3>3.2 最终做了什么？</font></h3><p>在此函数中会判断CPU的类型，如果 CPU 为 MX6SX、 MX6UL、 MX6ULL 或 MX6SLL中的任意一种，那么就会直接返回，相当于s_init 函数什么都没做。所以对于 I.MX6UL&#x2F;I.MX6ULL 来说， s_init 就是个空函数。从 s_init 函数退出以后进入函数 lowlevel_init，但是 lowlevel_init 函数也执行完成了，返回到了函数 cpu_init_crit，函数 cpu_init_crit 也执行完成了，最终返回到 save_boot_params_ret。</p>
<h2 id="4-main-函数"><a href="#4-main-函数" class="headerlink" title="4. _main 函数"></a><font size=3>4. _main 函数</font></h2><h3 id="4-1-main-函数在哪里？"><a href="#4-1-main-函数在哪里？" class="headerlink" title="4.1 _main 函数在哪里？"></a><font size=3>4.1 _main 函数在哪里？</font></h3><p>经过前边的分析，save_boot_params_ret 函数最后会跳转到 _main 函数，这个函数定义在 uboot 源码的这个文件中：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">arch/arm/lib/crt0.S</span><br></pre></td></tr></table></figure>

<p>我们打开这个文件，会看到如下内容：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line">ENTRY(_main)</span><br><span class="line"></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment"> * Set up initial C runtime environment and call board_init_f(0).</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">if</span> defined(CONFIG_SPL_BUILD) &amp;&amp; defined(CONFIG_SPL_STACK)</span></span><br><span class="line">	ldr	sp, =(CONFIG_SPL_STACK)</span><br><span class="line"><span class="meta">#<span class="keyword">else</span></span></span><br><span class="line">	ldr	sp, =(CONFIG_SYS_INIT_SP_ADDR)</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">if</span> defined(CONFIG_CPU_V7M)	<span class="comment">/* v7M forbids using SP as BIC destination */</span></span></span><br><span class="line">	mov	r3, sp</span><br><span class="line">	bic	r3, r3, #<span class="number">7</span></span><br><span class="line">	mov	sp, r3</span><br><span class="line"><span class="meta">#<span class="keyword">else</span></span></span><br><span class="line">	bic	sp, sp, #<span class="number">7</span>	<span class="comment">/* 8-byte alignment for ABI compliance */</span></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line">	mov	r0, sp</span><br><span class="line">	bl	board_init_f_alloc_reserve</span><br><span class="line">	mov	sp, r0</span><br><span class="line">	<span class="comment">/* set up gd here, outside any C code */</span></span><br><span class="line">	mov	r9, r0</span><br><span class="line">	bl	board_init_f_init_reserve</span><br><span class="line"></span><br><span class="line">	mov	r0, #<span class="number">0</span></span><br><span class="line">	bl	board_init_f</span><br><span class="line"><span class="comment">/* 后边的省略 ... ... */</span></span><br></pre></td></tr></table></figure>

<h3 id="4-2-做了什么？"><a href="#4-2-做了什么？" class="headerlink" title="4.2 做了什么？"></a><font size=3>4.2 做了什么？</font></h3><p>在 arch&#x2F;arm&#x2F;lib&#x2F;crt0.S 文件开头，其实已经说明了 _main 函数主要做了那些事情。</p>
<ul>
<li>（1）设置调用 board_init_f() 的初始环境。</li>
<li>（2）调用board_init_f()。</li>
<li>（3）设置中间环境，其中堆栈和GD是由系统RAM中的board_init_f()分配的，但BSS和初始化的非const数据仍然不可用。</li>
<li>（4）对于U-Boot(不是SPL)，调用relocate_code()。对于SPL, board_init_f()只返回(到crt0)。</li>
<li>（5）设置调用board_init_r()的最终环境。</li>
<li>（6）对于U-Boot本身(不是SPL)，一些cpu在内存方面还有一些工作要做，因此调用 c_runtime_cpu_setup 。</li>
<li>（7）调用board_init_r()。</li>
</ul>
<h4 id="4-2-1-设置初始C运行时环境"><a href="#4-2-1-设置初始C运行时环境" class="headerlink" title="4.2.1 设置初始C运行时环境"></a><font size=3>4.2.1 设置初始C运行时环境</font></h4><p>以下部分为 arch&#x2F;arm&#x2F;lib&#x2F;crt0.S 文件的 69 - 93 行代码</p>
<figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line">/*</span><br><span class="line"> * Set up initial C runtime environment and call board_init_f(0).</span><br><span class="line"> */</span><br><span class="line"></span><br><span class="line">#if defined(CONFIG_SPL_BUILD) &amp;&amp; defined(CONFIG_SPL_STACK)</span><br><span class="line">	ldr	sp, =(CONFIG_SPL_STACK)</span><br><span class="line">#else</span><br><span class="line">	ldr	sp, =(CONFIG_SYS_INIT_SP_ADDR)</span><br><span class="line">#endif</span><br><span class="line">#if defined(CONFIG_CPU_V7M)	/* v7M forbids using SP as BIC destination */</span><br><span class="line">	mov	r3, sp</span><br><span class="line">	bic	r3, r3, #7</span><br><span class="line">	mov	sp, r3</span><br><span class="line">#else</span><br><span class="line">	bic	sp, sp, #7	/* 8-byte alignment for ABI compliance */</span><br><span class="line">#endif</span><br><span class="line">	mov	r0, sp</span><br><span class="line">	bl	board_init_f_alloc_reserve</span><br><span class="line">	mov	sp, r0</span><br><span class="line">	/* set up gd here, outside any C code */</span><br><span class="line">	mov	r9, r0</span><br><span class="line">	bl	board_init_f_init_reserve</span><br><span class="line"></span><br><span class="line">	mov	r0, #0</span><br><span class="line">	bl	board_init_f</span><br></pre></td></tr></table></figure>

<p>第 8 行（第 76 行）：设置 sp 指针为 CONFIG_SYS_INIT_SP_ADDR，也就是 sp 指向 0X0091FF00。  </p>
<p>第 15 行（第 83 行）：sp 做 8 字节对齐。 </p>
<p>第 17 行（第 85 行）：读取 sp 到寄存器 r0 里面，此时 r0&#x3D;0X0091FF00。  </p>
<p>第 18 行（第 86 行）：调用函数 board_init_f_alloc_reserve，此函数有一个参数，参数为 r0 中的值，也就是 0X0091FF00 ，该函数主要是留出早期的 malloc 内存区域和 gd 内存区域。</p>
<details class="folding-tag" blue><summary> 点击查看 board_init_f_alloc_reserve 函数 </summary>
              <div class='content'>
              <p>此函数定义在 common&#x2F;init&#x2F;board_init.c  文件中：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">ulong <span class="title function_">board_init_f_alloc_reserve</span><span class="params">(ulong top)</span></span><br><span class="line">&#123;</span><br><span class="line">	<span class="comment">/* Reserve early malloc arena */</span></span><br><span class="line"><span class="meta">#<span class="keyword">if</span> defined(CONFIG_SYS_MALLOC_F)</span></span><br><span class="line">	top -= CONFIG_SYS_MALLOC_F_LEN;</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line">	<span class="comment">/* LAST : reserve GD (rounded up to a multiple of 16 bytes) */</span></span><br><span class="line">	top = rounddown(top-<span class="keyword">sizeof</span>(<span class="keyword">struct</span> global_data), <span class="number">16</span>);</span><br><span class="line"></span><br><span class="line">	<span class="keyword">return</span> top;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>函数 board_init_f_alloc_reserve 主要是留出早期的 malloc 内存区域和 gd 内存区域，其中 CONFIG_SYS_MALLOC_F_LEN&#x3D;0X400（ 在文件 include&#x2F;generated&#x2F;autoconf.h 中定义 ) ，sizeof(struct global_data)&#x3D;248（GD_SIZE 值），完成以后的内存分布如下图：</p><img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV05-uboot%E4%B8%8E%E5%86%85%E6%A0%B8/LV05-01-uboot-04-uboot%E5%90%AF%E5%8A%A8%E6%B5%81%E7%A8%8B/img/image-20221015143824906.png" alt="image-20221015143824906" style="zoom:50%;" /><p>函数 board_init_f_alloc_reserve 是有返回值的，返回值为新的 top 值，此时 top&#x3D;0X0091FA00。  </p>
              </div>
            </details>

<p>第 19 行（第 87 行）：将 r0 写入到 sp 里面，r0 保存着函数 board_init_f_alloc_reserve 的返回值，所以这一句也就是设置 sp &#x3D; 0X0091FA00。  </p>
<p>第 21 行（第 89 行）：将 r0 寄存器的值写到寄存器 r9 里面，因为 r9 寄存器存放着全局变量 gd 的地址。所以这一句其实就是让 gd 指向 0X0091FA00 。</p>
<details class="folding-tag" blue><summary> 点击查看 gd 地址 </summary>
              <div class='content'>
              <p>在文件 arch&#x2F;arm&#x2F;include&#x2F;asm&#x2F;global_data.h  中有如下宏定义：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">ifdef</span> CONFIG_ARM64</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> DECLARE_GLOBAL_DATA_PTR		register volatile gd_t *gd asm (<span class="string">&quot;x18&quot;</span>)</span></span><br><span class="line"><span class="meta">#<span class="keyword">else</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> DECLARE_GLOBAL_DATA_PTR		register volatile gd_t *gd asm (<span class="string">&quot;r9&quot;</span>)</span></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br></pre></td></tr></table></figure><p>可以看出， uboot 中定义了一个指向 gd_t 的指针 gd， gd 是存放在寄存器 r9 里面的，因此 gd 是个全局变量。 另外 gd_t 是个结构体， 这个结构体定义在 uboot 源码的这个文件中：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">include/asm-generic/global_data.h</span><br></pre></td></tr></table></figure><p> 我们打开这个文件，可以看到这个结构体定义如下：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> <span class="title">global_data</span> &#123;</span></span><br><span class="line">	<span class="type">bd_t</span> *bd;</span><br><span class="line">	<span class="type">unsigned</span> <span class="type">long</span> flags;</span><br><span class="line">	<span class="type">unsigned</span> <span class="type">int</span> baudrate;</span><br><span class="line">	<span class="type">unsigned</span> <span class="type">long</span> cpu_clk;	<span class="comment">/* CPU clock in Hz!		*/</span></span><br><span class="line">	<span class="type">unsigned</span> <span class="type">long</span> bus_clk;</span><br><span class="line">	<span class="comment">/* We cannot bracket this with CONFIG_PCI due to mpc5xxx */</span></span><br><span class="line">	<span class="type">unsigned</span> <span class="type">long</span> pci_clk;</span><br><span class="line">	<span class="type">unsigned</span> <span class="type">long</span> mem_clk;</span><br><span class="line">	<span class="comment">// 中间部分省略 ... ...</span></span><br><span class="line">&#125; <span class="type">gd_t</span>;</span><br></pre></td></tr></table></figure><p>由此可知，这一行代码就是设置 gd 所指向的位置，也就是 gd 指向 0X0091FA00。  </p>
              </div>
            </details>

<p>第 22 行（第 90 行）：调用函数 board_init_f_init_reserve ，该函数主要是用于初始化 gd，其实就是清 0 处理。</p>
<details class="folding-tag" blue><summary> 点击查看 board_init_f_init_reserve </summary>
              <div class='content'>
              <p>此函数在文件 common&#x2F;init&#x2F;board_init.c 中有定义 ：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">void</span> <span class="title function_">board_init_f_init_reserve</span><span class="params">(ulong base)</span></span><br><span class="line">&#123;</span><br><span class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">global_data</span> *<span class="title">gd_ptr</span>;</span></span><br><span class="line"><span class="meta">#<span class="keyword">ifndef</span> _USE_MEMCPY</span></span><br><span class="line">	<span class="type">int</span> *ptr;</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"></span><br><span class="line">	<span class="comment">/*</span></span><br><span class="line"><span class="comment">	 * clear GD entirely and set it up.</span></span><br><span class="line"><span class="comment">	 * Use gd_ptr, as gd may not be properly set yet.</span></span><br><span class="line"><span class="comment">	 */</span></span><br><span class="line"></span><br><span class="line">	gd_ptr = (<span class="keyword">struct</span> global_data *)base;</span><br><span class="line">	<span class="comment">/* zero the area */</span></span><br><span class="line"><span class="meta">#<span class="keyword">ifdef</span> _USE_MEMCPY</span></span><br><span class="line">	<span class="built_in">memset</span>(gd_ptr, <span class="string">&#x27;\0&#x27;</span>, <span class="keyword">sizeof</span>(*gd));</span><br><span class="line"><span class="meta">#<span class="keyword">else</span></span></span><br><span class="line">	<span class="keyword">for</span> (ptr = (<span class="type">int</span> *)gd_ptr; ptr &lt; (<span class="type">int</span> *)(gd_ptr + <span class="number">1</span>); )</span><br><span class="line">		*ptr++ = <span class="number">0</span>;</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line">	<span class="comment">/* set GD unless architecture did it already */</span></span><br><span class="line"><span class="meta">#<span class="keyword">if</span> !defined(CONFIG_ARM)</span></span><br><span class="line">	arch_setup_gd(gd_ptr);</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line">	<span class="comment">/* next alloc will be higher by one GD plus 16-byte alignment */</span></span><br><span class="line">	base += roundup(<span class="keyword">sizeof</span>(<span class="keyword">struct</span> global_data), <span class="number">16</span>);</span><br><span class="line"></span><br><span class="line">	<span class="comment">/*</span></span><br><span class="line"><span class="comment">	 * record early malloc arena start.</span></span><br><span class="line"><span class="comment">	 * Use gd as it is now properly set for all architectures.</span></span><br><span class="line"><span class="comment">	 */</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">if</span> defined(CONFIG_SYS_MALLOC_F)</span></span><br><span class="line">	<span class="comment">/* go down one &#x27;early malloc arena&#x27; */</span></span><br><span class="line">	gd-&gt;malloc_base = base;</span><br><span class="line">	<span class="comment">/* next alloc will be higher by one &#x27;early malloc arena&#x27; size */</span></span><br><span class="line">	base += CONFIG_SYS_MALLOC_F_LEN;</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>可以看出，此函数用于初始化 gd，其实就是清零处理。另外，此函数还设置了 gd-&gt;malloc_base 为 gd 基地址+gd 大小 &#x3D; 0X0091FA00 + 248 &#x3D; 0X0091FAF8，再做 16 字节对齐，最终 gd-&gt;malloc_base &#x3D; 0X0091FB00，这个也就是 early malloc 的起始地址。  </p>
              </div>
            </details>

<p>第 25 行（第 93 行）：调用 board_init_f 函数，这个函数定义在文件 common&#x2F;board_f.c 中，主要用来初始化 DDR，定时器，完成代码拷贝等，后边我们会再详细分析这个函数。</p>
<h4 id="4-2-2-设置中间环境"><a href="#4-2-2-设置中间环境" class="headerlink" title="4.2.2 设置中间环境"></a><font size=3>4.2.2 设置中间环境</font></h4><p>以下部分为 arch&#x2F;arm&#x2F;lib&#x2F;crt0.S 文件的 97 - 127 行代码</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line">/*</span><br><span class="line"> * Set up intermediate environment (new sp and gd) and call</span><br><span class="line"> * relocate_code(addr_moni). Trick here is that we&#x27;ll return</span><br><span class="line"> * &#x27;here&#x27; but relocated.</span><br><span class="line"> */</span><br><span class="line"></span><br><span class="line">	ldr	sp, [r9, #GD_START_ADDR_SP]	/* sp = gd-&gt;start_addr_sp */</span><br><span class="line">#if defined(CONFIG_CPU_V7M)	/* v7M forbids using SP as BIC destination */</span><br><span class="line">	mov	r3, sp</span><br><span class="line">	bic	r3, r3, #7</span><br><span class="line">	mov	sp, r3</span><br><span class="line">#else</span><br><span class="line">	bic	sp, sp, #7	/* 8-byte alignment for ABI compliance */</span><br><span class="line">#endif</span><br><span class="line">	ldr	r9, [r9, #GD_BD]		/* r9 = gd-&gt;bd */</span><br><span class="line">	sub	r9, r9, #GD_SIZE		/* new GD is below bd */</span><br><span class="line"></span><br><span class="line">	adr	lr, here</span><br><span class="line">	ldr	r0, [r9, #GD_RELOC_OFF]		/* r0 = gd-&gt;reloc_off */</span><br><span class="line">	add	lr, lr, r0</span><br><span class="line">#if defined(CONFIG_CPU_V7M)</span><br><span class="line">	orr	lr, #1				/* As required by Thumb-only */</span><br><span class="line">#endif</span><br><span class="line">	ldr	r0, [r9, #GD_RELOCADDR]		/* r0 = gd-&gt;relocaddr */</span><br><span class="line">	b	relocate_code</span><br><span class="line">here:</span><br><span class="line">/*</span><br><span class="line"> * now relocate vectors</span><br><span class="line"> */</span><br><span class="line"></span><br><span class="line">	bl	relocate_vectors</span><br></pre></td></tr></table></figure>

<p>第 13 行（第 109 行）：sp 做 8 字节对齐。  </p>
<p>第 15 行（第 111 行）：获取 gd-&gt;bd 的地址赋给 r9，此时 r9 存放的是之前的 gd，这里通过获取 gd-&gt;bd 的地址来计算出新的 gd 的位置。 </p>
<details class="folding-tag" blue><summary> 点击查看 GD_BD 的值 </summary>
              <div class='content'>
              <p>GD_BD 定义在文件 include&#x2F;generated&#x2F;generic-asm-offsets.h 中：  </p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">ifndef</span> __GENERIC_ASM_OFFSETS_H__</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> __GENERIC_ASM_OFFSETS_H__</span></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment"> * DO NOT MODIFY.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * This file was generated by Kbuild</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> GENERATED_GBL_DATA_SIZE 256 <span class="comment">/* (sizeof(struct global_data) + 15) &amp; ~15	@ */</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> GENERATED_BD_INFO_SIZE 80 <span class="comment">/* (sizeof(struct bd_info) + 15) &amp; ~15	@ */</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> GD_SIZE 248 <span class="comment">/* sizeof(struct global_data)	@ */</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> GD_BD 0 <span class="comment">/* offsetof(struct global_data, bd)	@ */</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> GD_MALLOC_BASE 192 <span class="comment">/* offsetof(struct global_data, malloc_base)	@ */</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> GD_RELOCADDR 48 <span class="comment">/* offsetof(struct global_data, relocaddr)	@ */</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> GD_RELOC_OFF 68 <span class="comment">/* offsetof(struct global_data, reloc_off)	@ */</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> GD_START_ADDR_SP 64 <span class="comment">/* offsetof(struct global_data, start_addr_sp)	@ */</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br></pre></td></tr></table></figure><p>所以 ，其实 GD_BD &#x3D; 0。</p>
              </div>
            </details>

<p>第 16 行（第 112 行）：新的 gd 在 bd 下面，所以 r9 减去 gd 的大小就是新的 gd 的位置，获取到新的 gd的位置以后赋值给 r9。  </p>
<p>第 18 行（第 114 行）：设置 lr 寄存器为 here，这样后面执行其他函数返回的时候就返回到了第 26 行（第 122 行）的 here 位置处。  </p>
<p>第 19 行（第 115 行）：读取 gd-&gt;reloc_off 的值复制给 r0 寄存器， GD_RELOC_OFF&#x3D;68。  </p>
<details class="folding-tag" blue><summary> 点击查看 GD_RELOC_OFF 的值 </summary>
              <div class='content'>
              <p>GD_RELOC_OFF 定义在文件 include&#x2F;generated&#x2F;generic-asm-offsets.h 中：  </p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">ifndef</span> __GENERIC_ASM_OFFSETS_H__</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> __GENERIC_ASM_OFFSETS_H__</span></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment"> * DO NOT MODIFY.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * This file was generated by Kbuild</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> GENERATED_GBL_DATA_SIZE 256 <span class="comment">/* (sizeof(struct global_data) + 15) &amp; ~15	@ */</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> GENERATED_BD_INFO_SIZE 80 <span class="comment">/* (sizeof(struct bd_info) + 15) &amp; ~15	@ */</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> GD_SIZE 248 <span class="comment">/* sizeof(struct global_data)	@ */</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> GD_BD 0 <span class="comment">/* offsetof(struct global_data, bd)	@ */</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> GD_MALLOC_BASE 192 <span class="comment">/* offsetof(struct global_data, malloc_base)	@ */</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> GD_RELOCADDR 48 <span class="comment">/* offsetof(struct global_data, relocaddr)	@ */</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> GD_RELOC_OFF 68 <span class="comment">/* offsetof(struct global_data, reloc_off)	@ */</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> GD_START_ADDR_SP 64 <span class="comment">/* offsetof(struct global_data, start_addr_sp)	@ */</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br></pre></td></tr></table></figure><p>所以 ，其实 GD_RELOC_OFF &#x3D; 68。</p>
              </div>
            </details>

<p>第 20 行（第 116 行）： lr 寄存器的值加上 r0 寄存器的值，重新赋值给 lr 寄存器。因为接下来要重定位代码，也就是把代码拷贝到新的地方去（现在的 uboot 存放的起始地址为 0X87800000，下面要将 uboot 拷贝到 DDR 最后面的地址空间出，将 0X87800000 开始的内存空出来），其中就包括here，因此 lr 中的 here 要使用重定位后的位置。、</p>
<p>第 24 行（第 120 行）：读取 gd-&gt;relocaddr 的值赋给 r0 寄存器，此时 r0 寄存器就保存着 uboot 要拷贝的目的地址，为 0X9FF47000。 GD_RELOCADDR&#x3D;48。</p>
<details class="folding-tag" blue><summary> 点击查看 GD_RELOCADDR 的值 </summary>
              <div class='content'>
              <p>GD_RELOCADDR 定义在文件 include&#x2F;generated&#x2F;generic-asm-offsets.h 中：  </p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">ifndef</span> __GENERIC_ASM_OFFSETS_H__</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> __GENERIC_ASM_OFFSETS_H__</span></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment"> * DO NOT MODIFY.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * This file was generated by Kbuild</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> GENERATED_GBL_DATA_SIZE 256 <span class="comment">/* (sizeof(struct global_data) + 15) &amp; ~15	@ */</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> GENERATED_BD_INFO_SIZE 80 <span class="comment">/* (sizeof(struct bd_info) + 15) &amp; ~15	@ */</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> GD_SIZE 248 <span class="comment">/* sizeof(struct global_data)	@ */</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> GD_BD 0 <span class="comment">/* offsetof(struct global_data, bd)	@ */</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> GD_MALLOC_BASE 192 <span class="comment">/* offsetof(struct global_data, malloc_base)	@ */</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> GD_RELOCADDR 48 <span class="comment">/* offsetof(struct global_data, relocaddr)	@ */</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> GD_RELOC_OFF 68 <span class="comment">/* offsetof(struct global_data, reloc_off)	@ */</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> GD_START_ADDR_SP 64 <span class="comment">/* offsetof(struct global_data, start_addr_sp)	@ */</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br></pre></td></tr></table></figure><p>所以 ，其实 GD_RELOCADDR &#x3D; 48。</p>
              </div>
            </details>

<p>第 25 行（第 121 行）：调用函数 relocate_code，也就是代码重定位函数，此函数负责将 uboot 拷贝到新的地方去，此函数定义在 arch&#x2F;arm&#x2F;lib&#x2F;relocate.S 中，后边会再详细分析这个函数。  </p>
<p>第 31 行（第 127 行）：调用函数 relocate_vectors，对中断向量表做重定位，此函数定义在文件 arch&#x2F;arm&#x2F;lib&#x2F;relocate.S 中，后边我们会再分析这个函数。</p>
<h4 id="4-2-3-设置最终环境"><a href="#4-2-3-设置最终环境" class="headerlink" title="4.2.3 设置最终环境"></a><font size=3>4.2.3 设置最终环境</font></h4><p>以下部分为 arch&#x2F;arm&#x2F;lib&#x2F;crt0.S 文件的 129 - 177 行代码，这部分其实是跟上边的 here 是一起的，只是这部分设置的是最终环境，就拆分开来分析了：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br></pre></td><td class="code"><pre><span class="line">/* Set up final (full) environment */</span><br><span class="line"></span><br><span class="line">	bl	c_runtime_cpu_setup	/* we still call old routine here */</span><br><span class="line">#endif</span><br><span class="line">#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_FRAMEWORK)</span><br><span class="line"># ifdef CONFIG_SPL_BUILD</span><br><span class="line">	/* Use a DRAM stack for the rest of SPL, if requested */</span><br><span class="line">	bl	spl_relocate_stack_gd</span><br><span class="line">	cmp	r0, #0</span><br><span class="line">	movne	sp, r0</span><br><span class="line">	movne	r9, r0</span><br><span class="line"># endif</span><br><span class="line">	ldr	r0, =__bss_start	/* this is auto-relocated! */</span><br><span class="line"></span><br><span class="line">#ifdef CONFIG_USE_ARCH_MEMSET</span><br><span class="line">	ldr	r3, =__bss_end		/* this is auto-relocated! */</span><br><span class="line">	mov	r1, #0x00000000		/* prepare zero to clear BSS */</span><br><span class="line"></span><br><span class="line">	subs	r2, r3, r0		/* r2 = memset len */</span><br><span class="line">	bl	memset</span><br><span class="line">#else</span><br><span class="line">	ldr	r1, =__bss_end		/* this is auto-relocated! */</span><br><span class="line">	mov	r2, #0x00000000		/* prepare zero to clear BSS */</span><br><span class="line"></span><br><span class="line">clbss_l:cmp	r0, r1			/* while not at end of BSS */</span><br><span class="line">#if defined(CONFIG_CPU_V7M)</span><br><span class="line">	itt	lo</span><br><span class="line">#endif</span><br><span class="line">	strlo	r2, [r0]		/* clear 32-bit BSS word */</span><br><span class="line">	addlo	r0, r0, #4		/* move to next */</span><br><span class="line">	blo	clbss_l</span><br><span class="line">#endif</span><br><span class="line"></span><br><span class="line">#if ! defined(CONFIG_SPL_BUILD)</span><br><span class="line">	bl coloured_LED_init</span><br><span class="line">	bl red_led_on</span><br><span class="line">#endif</span><br><span class="line">	/* call board_init_r(gd_t *id, ulong dest_addr) */</span><br><span class="line">	mov     r0, r9                  /* gd_t */</span><br><span class="line">	ldr	r1, [r9, #GD_RELOCADDR]	/* dest_addr */</span><br><span class="line">	/* call board_init_r */</span><br><span class="line">#if defined(CONFIG_SYS_THUMB_BUILD)</span><br><span class="line">	ldr	lr, =board_init_r	/* this is auto-relocated! */</span><br><span class="line">	bx	lr</span><br><span class="line">#else</span><br><span class="line">	ldr	pc, =board_init_r	/* this is auto-relocated! */</span><br><span class="line">#endif</span><br><span class="line">	/* we should not return here. */</span><br><span class="line">#endif</span><br></pre></td></tr></table></figure>

<p>第 3 行（第 131 行）：调用函数 c_runtime_cpu_setup。</p>
<details class="folding-tag" blue><summary> 点击查看 c_runtime_cpu_setup 函数 </summary>
              <div class='content'>
              <p>此函数定义在文件arch&#x2F;arm&#x2F;cpu&#x2F;armv7&#x2F;start.S 中 ：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">ENTRY(c_runtime_cpu_setup)</span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment"> * If I-cache is enabled invalidate it</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">#<span class="keyword">ifndef</span> CONFIG_SYS_ICACHE_OFF</span></span><br><span class="line">	mcr	p15, <span class="number">0</span>, r0, c7, c5, <span class="number">0</span>	@ invalidate icache</span><br><span class="line">	mcr     p15, <span class="number">0</span>, r0, c7, c10, <span class="number">4</span>	@ DSB</span><br><span class="line">	mcr     p15, <span class="number">0</span>, r0, c7, c5, <span class="number">4</span>	@ ISB</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"></span><br><span class="line">	bx	lr</span><br><span class="line"></span><br><span class="line"><span class="title function_">ENDPROC</span><span class="params">(c_runtime_cpu_setup)</span></span><br></pre></td></tr></table></figure>
              </div>
            </details>

<p>第 13 ~ 31 行（第 141~159 行）：清除 BSS 段。  </p>
<p>第 39 行（第 167 行）：设置函数 board_init_r 的两个参数，board_init_r 函数第一个参数是 gd，因此读取 r9 保存到 r0 里面。</p>
<details class="folding-tag" blue><summary> 点击查看 board_init_r 函数 </summary>
              <div class='content'>
              <p>函数 board_init_r 声明如下 ：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">board_init_r(<span class="type">gd_t</span> *id, ulong dest_addr)</span><br></pre></td></tr></table></figure><p>第一个参数是 gd，因此读取 r9 保存到 r0 里面。</p>
              </div>
            </details>

<p>第 40 行（第 168 行）：函数 board_init_r 的第二个参数是目的地址，因此 r1&#x3D; gd-&gt;relocaddr。</p>
<p>第 46 行（第 174 行）：调用函数 board_init_r，此函数定义在文件 common&#x2F;board_r.c 中，后边会再详细分析这个函数。  </p>
<p>这个就是_main 函数的运行流程，在_main 函数里面调用了 board_init_f、 relocate_code、relocate_vectors 和 board_init_r 这 4 个函数，</p>
<h2 id="5-board-init-f-函数"><a href="#5-board-init-f-函数" class="headerlink" title="5. board_init_f 函数"></a><font size=3>5. board_init_f 函数</font></h2><h3 id="5-1-board-init-f-函数在哪里？"><a href="#5-1-board-init-f-函数在哪里？" class="headerlink" title="5.1 board_init_f 函数在哪里？"></a><font size=3>5.1 board_init_f 函数在哪里？</font></h3><p>上边我们分析 _main 函数的时候，它调用这个函数，board_init_f 函数主要有两个工作 ：</p>
<p>（1）初始化一系列外设，比如串口、定时器，或者打印一些消息等。</p>
<p>（2）初始化 gd 的各个成员变量， uboot 会将自己重定位到 DRAM 最后面的地址区域，也就是将自己拷贝到 DRAM 最后面的内存区域中。这么做的目的是给 Linux 腾出空间，防止 Linux kernel 覆盖掉 uboot，将 DRAM 前面的区域完整的空出来。在拷贝之前肯定要给 uboot 各部分分配好内存位置和大小，比如 gd 应该存放到哪个位置， malloc 内存池应该存放到哪个位置等等。这些信息都保存在 gd 的成员变量中，因此要对 gd 的这些成员变量做初始化。最终形成一个完整的内存“分配图”，在后面重定位 uboot 的时候就会用到这个内存“分配图”。  </p>
<p>这个函数定义在 uboot 源码中的这个文件中：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">common/board_f.c</span><br></pre></td></tr></table></figure>

<p>我们打开这个文件，可以看到函数如下：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">void</span> <span class="title function_">board_init_f</span><span class="params">(ulong boot_flags)</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="meta">#<span class="keyword">ifdef</span> CONFIG_SYS_GENERIC_GLOBAL_DATA</span></span><br><span class="line">	<span class="comment">/*</span></span><br><span class="line"><span class="comment">	 * For some archtectures, global data is initialized and used before</span></span><br><span class="line"><span class="comment">	 * calling this function. The data should be preserved. For others,</span></span><br><span class="line"><span class="comment">	 * CONFIG_SYS_GENERIC_GLOBAL_DATA should be defined and use the stack</span></span><br><span class="line"><span class="comment">	 * here to host global data until relocation.</span></span><br><span class="line"><span class="comment">	 */</span></span><br><span class="line">	<span class="type">gd_t</span> data;</span><br><span class="line"></span><br><span class="line">	gd = &amp;data;</span><br><span class="line"></span><br><span class="line">	<span class="comment">/*</span></span><br><span class="line"><span class="comment">	 * Clear global data before it is accessed at debug print</span></span><br><span class="line"><span class="comment">	 * in initcall_run_list. Otherwise the debug print probably</span></span><br><span class="line"><span class="comment">	 * get the wrong vaule of gd-&gt;have_console.</span></span><br><span class="line"><span class="comment">	 */</span></span><br><span class="line">	zero_global_data();</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"></span><br><span class="line">	gd-&gt;flags = boot_flags;</span><br><span class="line">	gd-&gt;have_console = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">	<span class="keyword">if</span> (initcall_run_list(init_sequence_f))</span><br><span class="line">		hang();</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">if</span> !defined(CONFIG_ARM) &amp;&amp; !defined(CONFIG_SANDBOX) &amp;&amp; \</span></span><br><span class="line"><span class="meta">		!defined(CONFIG_EFI_APP)</span></span><br><span class="line">	<span class="comment">/* NOTREACHED - jump_to_copy() does not return */</span></span><br><span class="line">	hang();</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="5-2-做了什么？"><a href="#5-2-做了什么？" class="headerlink" title="5.2 做了什么？"></a><font size=3>5.2 做了什么？</font></h3><p>函数实现如下：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">void</span> <span class="title function_">board_init_f</span><span class="params">(ulong boot_flags)</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="meta">#<span class="keyword">ifdef</span> CONFIG_SYS_GENERIC_GLOBAL_DATA</span></span><br><span class="line">	<span class="comment">/*</span></span><br><span class="line"><span class="comment">	 * For some archtectures, global data is initialized and used before</span></span><br><span class="line"><span class="comment">	 * calling this function. The data should be preserved. For others,</span></span><br><span class="line"><span class="comment">	 * CONFIG_SYS_GENERIC_GLOBAL_DATA should be defined and use the stack</span></span><br><span class="line"><span class="comment">	 * here to host global data until relocation.</span></span><br><span class="line"><span class="comment">	 */</span></span><br><span class="line">	<span class="type">gd_t</span> data;</span><br><span class="line"></span><br><span class="line">	gd = &amp;data;</span><br><span class="line"></span><br><span class="line">	<span class="comment">/*</span></span><br><span class="line"><span class="comment">	 * Clear global data before it is accessed at debug print</span></span><br><span class="line"><span class="comment">	 * in initcall_run_list. Otherwise the debug print probably</span></span><br><span class="line"><span class="comment">	 * get the wrong vaule of gd-&gt;have_console.</span></span><br><span class="line"><span class="comment">	 */</span></span><br><span class="line">	zero_global_data();</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"></span><br><span class="line">	gd-&gt;flags = boot_flags;</span><br><span class="line">	gd-&gt;have_console = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">	<span class="keyword">if</span> (initcall_run_list(init_sequence_f))</span><br><span class="line">		hang();</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">if</span> !defined(CONFIG_ARM) &amp;&amp; !defined(CONFIG_SANDBOX) &amp;&amp; \</span></span><br><span class="line"><span class="meta">		!defined(CONFIG_EFI_APP)</span></span><br><span class="line">	<span class="comment">/* NOTREACHED - jump_to_copy() does not return */</span></span><br><span class="line">	hang();</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>因为没有定义 CONFIG_SYS_GENERIC_GLOBAL_DATA，所以第 3 ~ 20 行（第 1037 ~ 1054 行）代码无效。  </p>
<p>第 22 行（第 1056 行）：初始化 gd-&gt;flags &#x3D; boot_flags &#x3D; 0。  </p>
<p>第 23 行（第 1057 行）：设置 gd-&gt;have_console&#x3D;0。  </p>
<p>第 25 行（第 1059 行）：通过函数 initcall_run_list 来运行初始化序列 init_sequence_f 里面的一些列函数， init_sequence_f 里面包含了一系列的初始化函数。这才是最重要的一个函数，由它完成的功能我们在 5.3 进行详细分析。</p>
<h3 id="5-3-init-sequence-f"><a href="#5-3-init-sequence-f" class="headerlink" title="5.3 init_sequence_f"></a><font size=3>5.3 init_sequence_f</font></h3><h4 id="5-3-1-数组成员"><a href="#5-3-1-数组成员" class="headerlink" title="5.3.1 数组成员"></a><font size=3>5.3.1 数组成员</font></h4><details class="folding-tag" blue><summary> 点击查看 init_sequence_f </summary>
              <div class='content'>
              <p>init_sequence_f 也是定义在文件 common&#x2F;board_f.c 中，由于 init_sequence_f 的内容比较长，里面有大量的条件编译代码，将条件编译部分删除掉了，去掉条件编译以后的 init_sequence_f 定义如下： </p> <figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">static</span> <span class="type">init_fnc_t</span> init_sequence_f[] = &#123;</span><br><span class="line">	setup_mon_len,</span><br><span class="line">	initf_malloc,</span><br><span class="line">	initf_console_record,</span><br><span class="line">	arch_cpu_init, <span class="comment">/* basic arch cpu dependent setup */</span></span><br><span class="line">	initf_dm,</span><br><span class="line">	arch_cpu_init_dm,</span><br><span class="line">	mark_bootstage, <span class="comment">/* need timer, go after init dm */</span></span><br><span class="line">	board_early_init_f,</span><br><span class="line">	timer_init, <span class="comment">/* initialize timer */</span></span><br><span class="line">	board_postclk_init,</span><br><span class="line">	get_clocks,</span><br><span class="line">	env_init, <span class="comment">/* initialize environment */</span></span><br><span class="line">	init_baud_rate, <span class="comment">/* initialze baudrate settings */</span></span><br><span class="line">	serial_init, <span class="comment">/* serial communications setup */</span></span><br><span class="line">	console_init_f, <span class="comment">/* stage 1 init of console */</span></span><br><span class="line">	display_options, <span class="comment">/* say that we are here */</span></span><br><span class="line">	display_text_info, <span class="comment">/* show debugging info if required */</span></span><br><span class="line">	print_cpuinfo, <span class="comment">/* display cpu info (and speed) */</span></span><br><span class="line">	show_board_info,</span><br><span class="line">	INIT_FUNC_WATCHDOG_INIT</span><br><span class="line">	INIT_FUNC_WATCHDOG_RESET</span><br><span class="line">	init_func_i2c,</span><br><span class="line">	announce_dram_init,</span><br><span class="line">	<span class="comment">/* <span class="doctag">TODO:</span> unify all these dram functions? */</span></span><br><span class="line">	dram_init, <span class="comment">/* configure available RAM banks */</span></span><br><span class="line">	post_init_f,</span><br><span class="line">	INIT_FUNC_WATCHDOG_RESET</span><br><span class="line">	testdram,</span><br><span class="line">	INIT_FUNC_WATCHDOG_RESET</span><br><span class="line">	INIT_FUNC_WATCHDOG_RESET</span><br><span class="line">	<span class="comment">/*</span></span><br><span class="line"><span class="comment">	 * Now that we have DRAM mapped and working, we can</span></span><br><span class="line"><span class="comment">	 * relocate the code and continue running from DRAM.</span></span><br><span class="line"><span class="comment">	 *</span></span><br><span class="line"><span class="comment">	 * Reserve memory at end of RAM for (top down in that order):</span></span><br><span class="line"><span class="comment">	 * - area that won&#x27;t get touched by U-Boot and Linux (optional)</span></span><br><span class="line"><span class="comment">	 * - kernel log buffer</span></span><br><span class="line"><span class="comment">	 * - protected RAM</span></span><br><span class="line"><span class="comment">	 * - LCD framebuffer</span></span><br><span class="line"><span class="comment">	 * - monitor code</span></span><br><span class="line"><span class="comment">	 * - board info struct</span></span><br><span class="line"><span class="comment">	 */</span></span><br><span class="line">	setup_dest_addr,</span><br><span class="line">	reserve_round_4k,</span><br><span class="line">	reserve_mmu,</span><br><span class="line">	reserve_trace,</span><br><span class="line">	reserve_uboot,</span><br><span class="line">	reserve_malloc,</span><br><span class="line">	reserve_board,</span><br><span class="line">	setup_machine,</span><br><span class="line">	reserve_global_data,</span><br><span class="line">	reserve_fdt,</span><br><span class="line">	reserve_arch,</span><br><span class="line">	reserve_stacks,</span><br><span class="line">	setup_dram_config,</span><br><span class="line">	show_dram_config,</span><br><span class="line">	display_new_sp,</span><br><span class="line">	INIT_FUNC_WATCHDOG_RESET</span><br><span class="line">	reloc_fdt,</span><br><span class="line">	setup_reloc,</span><br><span class="line">	<span class="literal">NULL</span>,</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
              </div>
            </details>

<h4 id="5-3-2-重要函数说明"><a href="#5-3-2-重要函数说明" class="headerlink" title="5.3.2 重要函数说明"></a><font size=3>5.3.2 重要函数说明</font></h4><details class="folding-tag" blue><summary> 点击查看重要函数功能分析 </summary>
              <div class='content'>
              <p>接下来分析以上函数执行完以后的结果：  </p><p>第 2 行：setup_mon_len 函数设置 gd 的 mon_len 成员变量，此处为__bss_end -_start，也就是整个代码的长度。 0X878A8E74 - 0x87800000 &#x3D; 0XA8E74，这个就是代码长度。</p><p>第 3 行： initf_malloc 函数初始化 gd 中跟 malloc 有关的成员变量，比如 malloc_limit，此函数会设置 gd-&gt;malloc_limit &#x3D; CONFIG_SYS_MALLOC_F_LEN&#x3D;0X400。 malloc_limit 表示 malloc 内存池大小。</p><p>第 4 行： initf_console_record ， 如 果 定 义 了 宏 CONFIG_CONSOLE_RECORD 和 宏 CONFIG_SYS_MALLOC_F_LEN 的话此函数就会调用函数 console_record_init，但是 IMX6ULL 的 uboot 没有定义宏 CONFIG_CONSOLE_RECORD，所以此函数直接返回 0。</p><p>第 5 行：arch_cpu_init 函数。</p><p>第 6 行：initf_dm 函数，驱动模型的一些初始化。</p><p>第 7 行：arch_cpu_init_dm 函数未实现。</p><p>第 8 行：mark_bootstage 函数应该是和什么标记有关的，具体没有深究过。</p><p>第 9 行： board_early_init_f 函数，板子相关的早期的一些初始化设置， I.MX6ULL 用来初始化串口的 IO 配置。</p><p>第 10 行： timer_init，初始化定时器， Cortex-A7 内核有一个定时器，这里初始化的就是 CortexA 内核的那个定时器。通过这个定时器来为 uboot 提供时间。就跟 Cortex-M 内核 Systick 定时器一样。关于 Cortex-A 内部定时器的详细内容，请参考文档《ARM ArchitectureReference Manual ARMv7-A and ARMv7-R edition》 的 Chapter B8 The Generic Timer 章节。</p><p>第 11 行： board_postclk_init，对于 I.MX6ULL 来说是设置 VDDSOC 电压。</p><p>第 12 行：get_clocks 函数用于获取一些时钟值， I.MX6ULL 获取的是 sdhc_clk 时钟，也就是 SD 卡外设的时钟。</p><p>第 13 行：env_init 函数是和环境变量有关的，设置 gd 的成员变量 env_addr，也就是环境变量的保存地址。</p><p>第 14 行：init_baud_rate 函数用于初始化波特率，根据环境变量 baudrate 来初始化 gd-&gt;baudrate。</p><p>第 15 行：serial_init，初始化串口。</p><p>第 16 行：console_init_f，设置 gd-&gt;have_console 为 1，表示有个控制台，此函数也将前面暂存在缓冲区中的数据通过控制台打印出来。  </p><p>第 17 行： display_options，通过串口输出一些信息 ，如：</p><img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV05-uboot%E4%B8%8E%E5%86%85%E6%A0%B8/LV05-01-uboot-04-uboot%E5%90%AF%E5%8A%A8%E6%B5%81%E7%A8%8B/img/image-20221015165952642.png" alt="image-20221015165952642" style="zoom:50%;" /><p>第 18 行： display_text_info，打印一些文本信息，如果开启 UBOOT 的 DEBUG 功能的话就会输出 text_base、 bss_start、 bss_end，形式如下：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">debug(<span class="string">&quot;U-Boot code: %08lX -&gt; %08lX BSS: -&gt; %08lX\n&quot;</span>,text_base, bss_start, bss_end);</span><br></pre></td></tr></table></figure><p>第 19 行： print_cpuinfo 函数用于打印 CPU 信息，如：</p><img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV05-uboot%E4%B8%8E%E5%86%85%E6%A0%B8/LV05-01-uboot-04-uboot%E5%90%AF%E5%8A%A8%E6%B5%81%E7%A8%8B/img/image-20221015170028569.png" alt="image-20221015170028569" style="zoom:50%;" /><p>第 20 行： show_board_info 函数用于打印板子信息，会调用 checkboard 函数，如：</p><img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV05-uboot%E4%B8%8E%E5%86%85%E6%A0%B8/LV05-01-uboot-04-uboot%E5%90%AF%E5%8A%A8%E6%B5%81%E7%A8%8B/img/image-20221015170057791.png" alt="image-20221015170057791" style="zoom:50%;" /><p>第 21 行：INIT_FUNC_WATCHDOG_INIT，初始化看门狗，对于 I.MX6ULL 来说是空函数。</p><p>第 22 行：INIT_FUNC_WATCHDOG_RESET，复位看门狗，对于 I.MX6ULL 来说是空函数。</p><p>第 23 行：init_func_i2c 函数用于初始化 I2C，初始化完成以后会输出如下信息：  </p><img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV05-uboot%E4%B8%8E%E5%86%85%E6%A0%B8/LV05-01-uboot-04-uboot%E5%90%AF%E5%8A%A8%E6%B5%81%E7%A8%8B/img/image-20221015170124394.png" alt="image-20221015170124394" style="zoom:50%;" /><p>第 24 行：announce_dram_init，这个函数很简单，就是输出字符串“DRAM:”</p><p>第 26 行：dram_init，并非真正的初始化 DDR，只是设置 gd-&gt;ram_size 的值，对于正点原子 I.MX6ULL 开发板 EMMC 版本核心板来说就是 512MB。</p><p>第 27 行： post_init_f，此函数用来完成一些测试，初始化 gd-&gt;post_init_f_time。</p><p>第 29 行：testdram，测试 DRAM，空函数。  </p><p>第 44 行：setup_dest_addr 函数，设置目的地址，设置 gd-&gt;ram_size， gd-&gt;ram_top， gd-&gt;relocaddr 这三个的值。</p><details class="folding-tag" blue><summary> 点击查看 这三个值的打印方法 </summary>              <div class='content'>              <p>接下来我们会遇到很多跟数值有关的设置，如果直接看代码分析的话就太费时间了，我们可以修改 uboot 代码，直接将这些值通过串口打印出来，比如这里我们修改文件 common&#x2F;board_f.c，因为 setup_dest_addr 函数定义在文件 common&#x2F;board_f.c 中，在setup_dest_addr函数输入如下图内容：</p><img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV05-uboot%E4%B8%8E%E5%86%85%E6%A0%B8/LV05-01-uboot-04-uboot%E5%90%AF%E5%8A%A8%E6%B5%81%E7%A8%8B/img/image-20221015170554062.png" alt="image-20221015170554062" style="zoom:50%;" /><p>设置好以后重新编译 uboot，然后烧写到 SD 卡中，选择 SD 卡启动，重启开发板，我们就会得到以下输出信息：</p><img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV05-uboot%E4%B8%8E%E5%86%85%E6%A0%B8/LV05-01-uboot-04-uboot%E5%90%AF%E5%8A%A8%E6%B5%81%E7%A8%8B/img/image-20221015171129255.png" alt="image-20221015171129255" style="zoom: 50%;" /><p>从图中可以看出：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">gd-&gt;ram_size = <span class="number">0X20000000</span>  <span class="comment">//ram 大小为 0X20000000=512MB</span></span><br><span class="line">gd-&gt;ram_top = <span class="number">0XA0000000</span>   <span class="comment">//ram 最高地址为 0X80000000+0X20000000=0XA0000000</span></span><br><span class="line">gd-&gt;relocaddr = <span class="number">0XA0000000</span> <span class="comment">//重定位后最高地址为 0XA0000000</span></span><br></pre></td></tr></table></figure>              </div>            </details> <p>第 45 行 ：reserve_round_4k 函 数 用 于 对 gd-&gt;relocaddr 做 4KB 对 齐 ， 因 为 gd-&gt;relocaddr&#x3D;0XA0000000，已经是 4K 对齐了，所以调整后不变。</p><p>第 46 行：reserve_mmu，留出 MMU 的 TLB 表的位置，分配 MMU 的 TLB 表内存以后会对 gd-&gt;relocaddr 做 64K 字节对齐。对齐之后有：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">gd-&gt;arch.tlb_size= <span class="number">0X4000</span>    <span class="comment">//MMU 的 TLB 表大小</span></span><br><span class="line">gd-&gt;arch.tlb_addr=<span class="number">0X9FFF0000</span> <span class="comment">//MMU 的 TLB 表起始地址， 64KB 对齐以后</span></span><br><span class="line">gd-&gt;relocaddr=<span class="number">0X9FFF0000</span>     <span class="comment">//relocaddr 地址</span></span><br></pre></td></tr></table></figure><p>第 47 行：reserve_trace 函数，留出跟踪调试的内存， I.MX6ULL 没有用到。</p><p>第 48 行： reserve_uboot， 留出重定位后的 uboot 所占用的内存区域， uboot 所占用大小由 gd-&gt;mon_len 所指定，留出 uboot 的空间以后还要对 gd-&gt;relocaddr 做 4K 字节对齐，并且重新设置 gd-&gt;start_addr_sp 。完成之后吗，相关的变量值为：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">gd-&gt;mon_len = <span class="number">0XA8EF4</span></span><br><span class="line">gd-&gt;start_addr_sp = <span class="number">0X9FF47000</span></span><br><span class="line">gd-&gt;relocaddr = <span class="number">0X9FF47000</span></span><br></pre></td></tr></table></figure><p>第 49 行：reserve_malloc，留出 malloc 区域，调整 gd-&gt;start_addr_sp 位置， malloc 区域由宏TOTAL_MALLOC_LEN 定义。调整之后，相关变量的值如下：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">TOTAL_MALLOC_LEN=<span class="number">0X1002000</span></span><br><span class="line">gd-&gt;start_addr_sp=<span class="number">0X9EF45000</span> <span class="comment">//0X9FF47000-16MB-8KB=0X9EF45000</span></span><br></pre></td></tr></table></figure><details class="folding-tag" blue><summary> 点击查看 TOTAL_MALLOC_LEN </summary>              <div class='content'>              <p>这个宏，定义在 include&#x2F;common.h 中：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">define</span>	TOTAL_MALLOC_LEN	(CONFIG_SYS_MALLOC_LEN + CONFIG_ENV_SIZE)</span></span><br></pre></td></tr></table></figure><p>在 include&#x2F;configs&#x2F;mx6ull_alientek_emmc.h 文件中定义宏 CONFIG_SYS_MALLOC_LEN 和 CONFIG_ENV_SIZE：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">define</span> CONFIG_ENV_SIZE			SZ_8K</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* Size of malloc() pool */</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> CONFIG_SYS_MALLOC_LEN		(16 * SZ_1M) <span class="comment">/* malloc 内存池大小，这里设置为 16MB */</span></span></span><br></pre></td></tr></table></figure><p>所以宏 CONFIG_SYS_MALLOC_LEN 为 16MB &#x3D; 0X1000000，宏 CONFIG_ENV_SIZE &#x3D; 8KB &#x3D; 0X2000，因此TOTAL_MALLOC_LEN &#x3D; 0X1002000。  </p>              </div>            </details><p>第 50 行：reserve_board 函数，留出板子 bd 所占的内存区， bd 是结构体 bd_t， bd_t 大小为 80 字节 。此时相关变量的值如下：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">gd-&gt;start_addr_sp=<span class="number">0X9EF44FB0</span></span><br><span class="line">gd-&gt;bd=<span class="number">0X9EF44FB0</span></span><br></pre></td></tr></table></figure><p>第 51 行： setup_machine，设置机器 ID， linux 启动的时候会和这个机器 ID 匹配，如果匹配的话 linux 就会启动正常。但是 I.MX6ULL 不用这种方式了，这是以前老版本的 uboot 和 linux 使用的，新版本使用设备树了，因此此函数无效。</p><p>第 52 行：reserve_global_data 函数，保留出 gd_t 的内存区域， gd_t 结构体大小为 248B。相关变量的值如下：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">gd-&gt;start_addr_sp=<span class="number">0X9EF44EB8</span> <span class="comment">//0X9EF44FB0-248=0X9EF44EB8</span></span><br><span class="line">gd-&gt;new_gd=<span class="number">0X9EF44EB8</span></span><br></pre></td></tr></table></figure><p>第 53 行：reserve_fdt，留出设备树相关的内存区域， I.MX6ULL 的 uboot 没有用到，因此此函数无效。</p><p>第 54 行：reserve_arch 是个空函数。</p><p>第 55 行： reserve_stacks，留出栈空间，先对 gd-&gt;start_addr_sp 减去 16，然后做 16 字节对齐。如果使能 IRQ 的话还要留出 IRQ 相应的内存，具体工作是由 arch&#x2F;arm&#x2F;lib&#x2F;stack.c 文件中的函数 arch_reserve_stacks 完成。在我们使用的这个 uboot 中并没有使用到 IRQ，所以不会留出 IRQ 相应的内存区域，此时gd-&gt;start_addr_sp 的值如下：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">gd-&gt;start_addr_sp=<span class="number">0X9EF44E90</span></span><br></pre></td></tr></table></figure><p>第 56 行： setup_dram_config 函数设置 dram 信息，就是设置 gd-&gt;bd-&gt;bi_dram[0].start 和 gd-&gt;bd-&gt;bi_dram[0].size，后面会传递给 linux 内核，告诉 linux DRAM 的起始地址和大小。我们可以在该函数中打印一下这几个变量，结果应该是下边这样：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">gd-&gt;bd-&gt;bi_dram[<span class="number">0</span>].start = <span class="number">0x80000000</span></span><br><span class="line">gd-&gt;bd-&gt;bi_dram[<span class="number">0</span>].size = <span class="number">0x20000000</span></span><br></pre></td></tr></table></figure><p>第 57 行， show_dram_config 函数，用于显示 DRAM 的配置 ，如：</p><img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV05-uboot%E4%B8%8E%E5%86%85%E6%A0%B8/LV05-01-uboot-04-uboot%E5%90%AF%E5%8A%A8%E6%B5%81%E7%A8%8B/img/image-20221015203536954.png" alt="image-20221015203536954" style="zoom:50%;" /><p>第 58 行：display_new_sp 函数，显示新的 sp 位置，也就是 gd-&gt;start_addr_sp，不过要定义宏 DEBUG 。</p><p>第 60 行：reloc_fdt 函数用于重定位 fdt，没有用到。</p><p>第 61 行： setup_reloc，设置 gd 的其他一些成员变量，供后面重定位的时候使用，并且将以前的 gd 拷贝到 gd-&gt;new_gd 处。需要使能 DEBUG 才能看到相应的信息输出 。这里我们要是打印这些信息的话，会发现uboot 重定位后的偏移为 0X18747000，重定位后的新地址为0X9FF4700，新的 gd 首地址为 0X9EF44EB8，最终的 sp 为 0X9EF44E90。  </p>
              </div>
            </details>  

<h4 id="5-3-3-最终内存分配"><a href="#5-3-3-最终内存分配" class="headerlink" title="5.3.3 最终内存分配"></a><font size=3>5.3.3 最终内存分配</font></h4><p>经过上边的一系列函数的执行，最终的内存分配如下图所示：</p>
<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV05-uboot%E4%B8%8E%E5%86%85%E6%A0%B8/LV05-01-uboot-04-uboot%E5%90%AF%E5%8A%A8%E6%B5%81%E7%A8%8B/img/image-20221015210430581.png" alt="image-20221015210430581" style="zoom:50%;" />

<h2 id="6-relocate-code-函数"><a href="#6-relocate-code-函数" class="headerlink" title="6. relocate_code 函数"></a><font size=3>6. relocate_code 函数</font></h2><h3 id="6-1-relocate-code-函数在哪里？"><a href="#6-1-relocate-code-函数在哪里？" class="headerlink" title="6.1 relocate_code 函数在哪里？"></a><font size=3>6.1 relocate_code 函数在哪里？</font></h3><p>上边我们分析 _main 函数的时候，它调用这个函数，<strong>relocate_code 函数是用于代码拷贝的</strong>，它定义在 uboot 源码中的这个文件中：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">arch/arm/lib/relocate.S</span><br></pre></td></tr></table></figure>

<p>我们打开这个文件，可以看到如下代码：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br></pre></td><td class="code"><pre><span class="line">/*</span><br><span class="line"> * void relocate_code(addr_moni)</span><br><span class="line"> *</span><br><span class="line"> * This function relocates the monitor code.</span><br><span class="line"> *</span><br><span class="line"> * NOTE:</span><br><span class="line"> * To prevent the code below from containing references with an R_ARM_ABS32</span><br><span class="line"> * relocation record type, we never refer to linker-defined symbols directly.</span><br><span class="line"> * Instead, we declare literals which contain their relative location with</span><br><span class="line"> * respect to relocate_code, and at run time, add relocate_code back to them.</span><br><span class="line"> */</span><br><span class="line"></span><br><span class="line">ENTRY(relocate_code)</span><br><span class="line">	ldr	r1, =__image_copy_start	/* r1 &lt;- SRC &amp;__image_copy_start */</span><br><span class="line">	subs	r4, r0, r1		/* r4 &lt;- relocation offset */</span><br><span class="line">	beq	relocate_done		/* skip relocation */</span><br><span class="line">	ldr	r2, =__image_copy_end	/* r2 &lt;- SRC &amp;__image_copy_end */</span><br><span class="line"></span><br><span class="line">copy_loop:</span><br><span class="line">	ldmia	r1!, &#123;r10-r11&#125;		/* copy from source address [r1]    */</span><br><span class="line">	stmia	r0!, &#123;r10-r11&#125;		/* copy to   target address [r0]    */</span><br><span class="line">	cmp	r1, r2			/* until source end address [r2]    */</span><br><span class="line">	blo	copy_loop</span><br><span class="line"></span><br><span class="line">	/*</span><br><span class="line">	 * fix .rel.dyn relocations</span><br><span class="line">	 */</span><br><span class="line">	ldr	r2, =__rel_dyn_start	/* r2 &lt;- SRC &amp;__rel_dyn_start */</span><br><span class="line">	ldr	r3, =__rel_dyn_end	/* r3 &lt;- SRC &amp;__rel_dyn_end */</span><br><span class="line">fixloop:</span><br><span class="line">	ldmia	r2!, &#123;r0-r1&#125;		/* (r0,r1) &lt;- (SRC location,fixup) */</span><br><span class="line">	and	r1, r1, #0xff</span><br><span class="line">	cmp	r1, #23			/* relative fixup? */</span><br><span class="line">	bne	fixnext</span><br><span class="line"></span><br><span class="line">	/* relative fix: increase location by offset */</span><br><span class="line">	add	r0, r0, r4</span><br><span class="line">	ldr	r1, [r0]</span><br><span class="line">	add	r1, r1, r4</span><br><span class="line">	str	r1, [r0]</span><br><span class="line">fixnext:</span><br><span class="line">	cmp	r2, r3</span><br><span class="line">	blo	fixloop</span><br><span class="line"></span><br><span class="line">relocate_done:</span><br><span class="line"></span><br><span class="line">#ifdef __XSCALE__</span><br><span class="line">	/*</span><br><span class="line">	 * On xscale, icache must be invalidated and write buffers drained,</span><br><span class="line">	 * even with cache disabled - 4.2.7 of xscale core developer&#x27;s manual</span><br><span class="line">	 */</span><br><span class="line">	mcr	p15, 0, r0, c7, c7, 0	/* invalidate icache */</span><br><span class="line">	mcr	p15, 0, r0, c7, c10, 4	/* drain write buffer */</span><br><span class="line">#endif</span><br><span class="line"></span><br><span class="line">	/* ARMv4- don&#x27;t know bx lr but the assembler fails to see that */</span><br><span class="line"></span><br><span class="line">#ifdef __ARM_ARCH_4__</span><br><span class="line">	mov	pc, lr</span><br><span class="line">#else</span><br><span class="line">	bx	lr</span><br><span class="line">#endif</span><br><span class="line"></span><br><span class="line">ENDPROC(relocate_code)</span><br></pre></td></tr></table></figure>

<h3 id="6-2-做了什么？"><a href="#6-2-做了什么？" class="headerlink" title="6.2 做了什么？"></a><font size=3>6.2 做了什么？</font></h3><p>我们的分析按下边的行号进行：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br></pre></td><td class="code"><pre><span class="line">ENTRY(relocate_code)</span><br><span class="line">	ldr	r1, =__image_copy_start	/* r1 &lt;- SRC &amp;__image_copy_start */</span><br><span class="line">	subs	r4, r0, r1		/* r4 &lt;- relocation offset */</span><br><span class="line">	beq	relocate_done		/* skip relocation */</span><br><span class="line">	ldr	r2, =__image_copy_end	/* r2 &lt;- SRC &amp;__image_copy_end */</span><br><span class="line"></span><br><span class="line">copy_loop:</span><br><span class="line">	ldmia	r1!, &#123;r10-r11&#125;		/* copy from source address [r1]    */</span><br><span class="line">	stmia	r0!, &#123;r10-r11&#125;		/* copy to   target address [r0]    */</span><br><span class="line">	cmp	r1, r2			/* until source end address [r2]    */</span><br><span class="line">	blo	copy_loop</span><br><span class="line"></span><br><span class="line">	/*</span><br><span class="line">	 * fix .rel.dyn relocations</span><br><span class="line">	 */</span><br><span class="line">	ldr	r2, =__rel_dyn_start	/* r2 &lt;- SRC &amp;__rel_dyn_start */</span><br><span class="line">	ldr	r3, =__rel_dyn_end	/* r3 &lt;- SRC &amp;__rel_dyn_end */</span><br><span class="line">fixloop:</span><br><span class="line">	ldmia	r2!, &#123;r0-r1&#125;		/* (r0,r1) &lt;- (SRC location,fixup) */</span><br><span class="line">	and	r1, r1, #0xff</span><br><span class="line">	cmp	r1, #23			/* relative fixup? */</span><br><span class="line">	bne	fixnext</span><br><span class="line"></span><br><span class="line">	/* relative fix: increase location by offset */</span><br><span class="line">	add	r0, r0, r4</span><br><span class="line">	ldr	r1, [r0]</span><br><span class="line">	add	r1, r1, r4</span><br><span class="line">	str	r1, [r0]</span><br><span class="line">fixnext:</span><br><span class="line">	cmp	r2, r3</span><br><span class="line">	blo	fixloop</span><br><span class="line"></span><br><span class="line">relocate_done:</span><br><span class="line"></span><br><span class="line">#ifdef __XSCALE__</span><br><span class="line">	/*</span><br><span class="line">	 * On xscale, icache must be invalidated and write buffers drained,</span><br><span class="line">	 * even with cache disabled - 4.2.7 of xscale core developer&#x27;s manual</span><br><span class="line">	 */</span><br><span class="line">	mcr	p15, 0, r0, c7, c7, 0	/* invalidate icache */</span><br><span class="line">	mcr	p15, 0, r0, c7, c10, 4	/* drain write buffer */</span><br><span class="line">#endif</span><br><span class="line"></span><br><span class="line">	/* ARMv4- don&#x27;t know bx lr but the assembler fails to see that */</span><br><span class="line"></span><br><span class="line">#ifdef __ARM_ARCH_4__</span><br><span class="line">	mov	pc, lr</span><br><span class="line">#else</span><br><span class="line">	bx	lr</span><br><span class="line">#endif</span><br><span class="line"></span><br><span class="line">ENDPROC(relocate_code)</span><br></pre></td></tr></table></figure>

<p>第 2 行（第 80 行）：r1&#x3D;__image_copy_start，也就是 r1 寄存器保存源地址，从第一节中的表一-3-1可知 __image_copy_start &#x3D; 0X87800000。  </p>
<p>第 3 行（第 81 行）：r0&#x3D;0X9FF47000，这个地址就是 uboot 拷贝的目标首地址。 r4 &#x3D; r0-r1 &#x3D; 0X9FF47000 - 0X87800000 &#x3D; 0X18747000，因此 r4 保存偏移量。  </p>
<p>第 4 行（第 82 行）：如果在第 3 行（第 81 行）中， r0-r1 等于 0，说明 r0 和 r1 相等，也就是源地址和目的地址是一样的，那肯定就不需要拷贝了，直接执行 relocate_done 函数。  </p>
<p>第 5 行（第 83 行）：r2&#x3D;__image_copy_end， r2 中保存拷贝之前的代码结束地址，由表一-3-1可知，__image_copy_end  &#x3D; 0x8785dd54。</p>
<p>第 6 行（第 84 行）：函数 copy_loop 完成代码拷贝工作！从 r1，也就是__image_copy_start 开始，读取 uboot 代码保存到 r10 和 r11 中，一次就只拷贝这 2 个 32 位的数据。拷贝完成以后 r1 的值会更新，保存下一个要拷贝的数据地址。</p>
<p>第 9 行（第 87 行）：将 r10 和 r11 的数据写到 r0 开始的地方，也就是目的地址。写完以后 r0 的值会更新，更新为下一个要写入的数据地址。  </p>
<p>第 10 行（第 88 行）：比较 r1 是否和 r2 相等，也就是检查是否拷贝完成，如果不相等的话说明没有拷贝完成， 没有拷贝完成的话就跳转到 copy_loop 接着拷贝，直至拷贝完成。  </p>
<p>第 16 ~ 31 行（第 94 ~ 109 行）：是重定位 .rel.dyn 段， .rel.dyn 段是存放 .text 段中需要重定位地址的集合。重定位就是 uboot 将自身拷贝到 DRAM 的另一个地方去继续运行（DRAM 的高地址处）。  我们知道，一个可执行的 bin 文件，其链接地址和运行地址要相等，也就是链接到哪个地址，在运行之前就要拷贝到哪个地址去。现在我们重定位以后，运行地址就和链接地址不同了，这样寻址的时候不会出问题吗？这个问题这里我就没有继续深入追究了，有兴趣的话可以看正点原子的驱动开发手册，里边有很详细的说明。</p>
<h2 id="7-relocate-vectors函数"><a href="#7-relocate-vectors函数" class="headerlink" title="7. relocate_vectors函数"></a><font size=3>7. relocate_vectors函数</font></h2><h3 id="7-1-relocate-vectors-函数在哪里？"><a href="#7-1-relocate-vectors-函数在哪里？" class="headerlink" title="7.1 relocate_vectors 函数在哪里？"></a><font size=3>7.1 relocate_vectors 函数在哪里？</font></h3><p>上边我们分析 _main 函数的时候，它调用这个函数，<strong>relocate_vectors 函数用于重定位向量表</strong>，它定义在 uboot 源码中的这个文件中：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">arch/arm/lib/relocate.S</span><br></pre></td></tr></table></figure>

<p>我们打开这个文件，可以看到如下代码：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line">ENTRY(relocate_vectors)</span><br><span class="line"></span><br><span class="line">#ifdef CONFIG_CPU_V7M</span><br><span class="line">	/*</span><br><span class="line">	 * On ARMv7-M we only have to write the new vector address</span><br><span class="line">	 * to VTOR register.</span><br><span class="line">	 */</span><br><span class="line">	ldr	r0, [r9, #GD_RELOCADDR]	/* r0 = gd-&gt;relocaddr */</span><br><span class="line">	ldr	r1, =V7M_SCB_BASE</span><br><span class="line">	str	r0, [r1, V7M_SCB_VTOR]</span><br><span class="line">#else</span><br><span class="line">#ifdef CONFIG_HAS_VBAR</span><br><span class="line">	/*</span><br><span class="line">	 * If the ARM processor has the security extensions,</span><br><span class="line">	 * use VBAR to relocate the exception vectors.</span><br><span class="line">	 */</span><br><span class="line">	ldr	r0, [r9, #GD_RELOCADDR]	/* r0 = gd-&gt;relocaddr */</span><br><span class="line">	mcr     p15, 0, r0, c12, c0, 0  /* Set VBAR */</span><br><span class="line">#else</span><br><span class="line">	/*</span><br><span class="line">	 * Copy the relocated exception vectors to the</span><br><span class="line">	 * correct address</span><br><span class="line">	 * CP15 c1 V bit gives us the location of the vectors:</span><br><span class="line">	 * 0x00000000 or 0xFFFF0000.</span><br><span class="line">	 */</span><br><span class="line">	ldr	r0, [r9, #GD_RELOCADDR]	/* r0 = gd-&gt;relocaddr */</span><br><span class="line">	mrc	p15, 0, r2, c1, c0, 0	/* V bit (bit[13]) in CP15 c1 */</span><br><span class="line">	ands	r2, r2, #(1 &lt;&lt; 13)</span><br><span class="line">	ldreq	r1, =0x00000000		/* If V=0 */</span><br><span class="line">	ldrne	r1, =0xFFFF0000		/* If V=1 */</span><br><span class="line">	ldmia	r0!, &#123;r2-r8,r10&#125;</span><br><span class="line">	stmia	r1!, &#123;r2-r8,r10&#125;</span><br><span class="line">	ldmia	r0!, &#123;r2-r8,r10&#125;</span><br><span class="line">	stmia	r1!, &#123;r2-r8,r10&#125;</span><br><span class="line">#endif</span><br><span class="line">#endif</span><br><span class="line">	bx	lr</span><br><span class="line"></span><br><span class="line">ENDPROC(relocate_vectors)</span><br></pre></td></tr></table></figure>

<h3 id="7-2-做了什么？"><a href="#7-2-做了什么？" class="headerlink" title="7.2 做了什么？"></a><font size=3>7.2 做了什么？</font></h3><p>我们的分析按下边的行号进行：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line">ENTRY(relocate_vectors)</span><br><span class="line"></span><br><span class="line">#ifdef CONFIG_CPU_V7M</span><br><span class="line">	/*</span><br><span class="line">	 * On ARMv7-M we only have to write the new vector address</span><br><span class="line">	 * to VTOR register.</span><br><span class="line">	 */</span><br><span class="line">	ldr	r0, [r9, #GD_RELOCADDR]	/* r0 = gd-&gt;relocaddr */</span><br><span class="line">	ldr	r1, =V7M_SCB_BASE</span><br><span class="line">	str	r0, [r1, V7M_SCB_VTOR]</span><br><span class="line">#else</span><br><span class="line">#ifdef CONFIG_HAS_VBAR</span><br><span class="line">	/*</span><br><span class="line">	 * If the ARM processor has the security extensions,</span><br><span class="line">	 * use VBAR to relocate the exception vectors.</span><br><span class="line">	 */</span><br><span class="line">	ldr	r0, [r9, #GD_RELOCADDR]	/* r0 = gd-&gt;relocaddr */</span><br><span class="line">	mcr     p15, 0, r0, c12, c0, 0  /* Set VBAR */</span><br><span class="line">#else</span><br><span class="line">	/*</span><br><span class="line">	 * Copy the relocated exception vectors to the</span><br><span class="line">	 * correct address</span><br><span class="line">	 * CP15 c1 V bit gives us the location of the vectors:</span><br><span class="line">	 * 0x00000000 or 0xFFFF0000.</span><br><span class="line">	 */</span><br><span class="line">	ldr	r0, [r9, #GD_RELOCADDR]	/* r0 = gd-&gt;relocaddr */</span><br><span class="line">	mrc	p15, 0, r2, c1, c0, 0	/* V bit (bit[13]) in CP15 c1 */</span><br><span class="line">	ands	r2, r2, #(1 &lt;&lt; 13)</span><br><span class="line">	ldreq	r1, =0x00000000		/* If V=0 */</span><br><span class="line">	ldrne	r1, =0xFFFF0000		/* If V=1 */</span><br><span class="line">	ldmia	r0!, &#123;r2-r8,r10&#125;</span><br><span class="line">	stmia	r1!, &#123;r2-r8,r10&#125;</span><br><span class="line">	ldmia	r0!, &#123;r2-r8,r10&#125;</span><br><span class="line">	stmia	r1!, &#123;r2-r8,r10&#125;</span><br><span class="line">#endif</span><br><span class="line">#endif</span><br><span class="line">	bx	lr</span><br><span class="line"></span><br><span class="line">ENDPROC(relocate_vectors)</span><br></pre></td></tr></table></figure>

<p>第 3 行（第 29 行）：如果定义了 CONFIG_CPU_V7M 的话就执行第 4 ~ 10 行（第 30 ~ 36 行）的代码，这是 Cortex-M 内核单片机执行的语句，因此对于 I.MX6ULL 来说是无效的。  </p>
<p>第 12 行（第 38 行）：如果定义了 CONFIG_HAS_VBAR 的话就执行此语句，这个是向量表偏移， CortexA7 是支持向量表偏移的。而且，在.config 里面定义了 CONFIG_HAS_VBAR，因此会执行这个分支。</p>
<p>第 17 行（第 43 行）：r0&#x3D;gd-&gt;relocaddr，也就是重定位后 uboot 的首地址，向量表肯定是从这个地址开始存放的。</p>
<p>第 18 行（第 44 行）：将 r0 的值写入到 CP15 的 VBAR 寄存器中，也就是将新的向量表首地址写入到寄存器 VBAR 中，设置向量表偏移。  </p>
<h2 id="8-board-init-r-函数"><a href="#8-board-init-r-函数" class="headerlink" title="8. board_init_r    函数"></a><font size=3>8. board_init_r    函数</font></h2><h3 id="8-1-board-init-r-函数在哪里？"><a href="#8-1-board-init-r-函数在哪里？" class="headerlink" title="8.1 board_init_r 函数在哪里？"></a><font size=3>8.1 board_init_r 函数在哪里？</font></h3><p>上边我们分析 _main 函数的时候，它调用这个函数，前边我们了解了 board_init_f 函数，在此函数里面会调用一系列的函数来初始化一些外设和 gd 的成员变量。但是 board_init_f 并没有初始化所有的外设，还需要做一些后续工作，这些后续工作就是由函数 board_init_r 来完成的  它定义在 uboot 源码中的这个文件中：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">common/board_r.c</span><br></pre></td></tr></table></figure>

<p>我们打开这个文件，可以看到如下代码：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line">void board_init_r(gd_t *new_gd, ulong dest_addr)</span><br><span class="line">&#123;</span><br><span class="line">#ifdef CONFIG_NEEDS_MANUAL_RELOC</span><br><span class="line">	int i;</span><br><span class="line">#endif</span><br><span class="line"></span><br><span class="line">#ifdef CONFIG_AVR32</span><br><span class="line">	mmu_init_r(dest_addr);</span><br><span class="line">#endif</span><br><span class="line"></span><br><span class="line">#if !defined(CONFIG_X86) &amp;&amp; !defined(CONFIG_ARM) &amp;&amp; !defined(CONFIG_ARM64)</span><br><span class="line">	gd = new_gd;</span><br><span class="line">#endif</span><br><span class="line"></span><br><span class="line">#ifdef CONFIG_NEEDS_MANUAL_RELOC</span><br><span class="line">	for (i = 0; i &lt; ARRAY_SIZE(init_sequence_r); i++)</span><br><span class="line">		init_sequence_r[i] += gd-&gt;reloc_off;</span><br><span class="line">#endif</span><br><span class="line"></span><br><span class="line">	if (initcall_run_list(init_sequence_r))</span><br><span class="line">		hang();</span><br><span class="line"></span><br><span class="line">	/* NOTREACHED - run_main_loop() does not return */</span><br><span class="line">	hang();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="8-2-做了什么？"><a href="#8-2-做了什么？" class="headerlink" title="8.2 做了什么？"></a><font size=3>8.2 做了什么？</font></h3><p>我们的分析按下边的行号进行：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line">void board_init_r(gd_t *new_gd, ulong dest_addr)</span><br><span class="line">&#123;</span><br><span class="line">#ifdef CONFIG_NEEDS_MANUAL_RELOC</span><br><span class="line">	int i;</span><br><span class="line">#endif</span><br><span class="line"></span><br><span class="line">#ifdef CONFIG_AVR32</span><br><span class="line">	mmu_init_r(dest_addr);</span><br><span class="line">#endif</span><br><span class="line"></span><br><span class="line">#if !defined(CONFIG_X86) &amp;&amp; !defined(CONFIG_ARM) &amp;&amp; !defined(CONFIG_ARM64)</span><br><span class="line">	gd = new_gd;</span><br><span class="line">#endif</span><br><span class="line"></span><br><span class="line">#ifdef CONFIG_NEEDS_MANUAL_RELOC</span><br><span class="line">	for (i = 0; i &lt; ARRAY_SIZE(init_sequence_r); i++)</span><br><span class="line">		init_sequence_r[i] += gd-&gt;reloc_off;</span><br><span class="line">#endif</span><br><span class="line"></span><br><span class="line">	if (initcall_run_list(init_sequence_r))</span><br><span class="line">		hang();</span><br><span class="line"></span><br><span class="line">	/* NOTREACHED - run_main_loop() does not return */</span><br><span class="line">	hang();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>第 20 行（第 1010 行）：调用 initcall_run_list 函数来执行初始化序列 init_sequence_r， init_sequence_r 是一个函数集合，init_sequence_r 也定义在文件 common&#x2F;board_r.c 中，我们下一小节详细学习。</p>
<h3 id="8-3-init-sequence-r"><a href="#8-3-init-sequence-r" class="headerlink" title="8.3 init_sequence_r "></a><font size=3>8.3 init_sequence_r </font></h3><h4 id="8-3-1-数组成员"><a href="#8-3-1-数组成员" class="headerlink" title="8.3.1 数组成员"></a><font size=3>8.3.1 数组成员</font></h4><details class="folding-tag" blue><summary> 点击查看 init_sequence_r </summary>
              <div class='content'>
              <p>init_sequence_r 也定义在文件 common&#x2F;board_r.c 中，由于 init_sequence_f 的内容比较长，里面有大量的条件编译代码，这里将条件编译部分删除掉了，去掉条件编译以后的 init_sequence_r 定义如下：  </p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">init_fnc_t</span> init_sequence_r[] = &#123;</span><br><span class="line">	initr_trace,</span><br><span class="line">	initr_reloc,</span><br><span class="line">	initr_caches,</span><br><span class="line">	initr_reloc_global_data,</span><br><span class="line">	initr_barrier,</span><br><span class="line">	initr_malloc,</span><br><span class="line">	initr_console_record,</span><br><span class="line">	bootstage_relocate,</span><br><span class="line">	initr_bootstage,</span><br><span class="line">	board_init, <span class="comment">/* Setup chipselects */</span></span><br><span class="line">	stdio_init_tables,</span><br><span class="line">	initr_serial,</span><br><span class="line">	initr_announce,</span><br><span class="line">	INIT_FUNC_WATCHDOG_RESET</span><br><span class="line">	INIT_FUNC_WATCHDOG_RESET</span><br><span class="line">	INIT_FUNC_WATCHDOG_RESET</span><br><span class="line">	power_init_board,</span><br><span class="line">	initr_flash,</span><br><span class="line">	INIT_FUNC_WATCHDOG_RESET</span><br><span class="line">	initr_nand,</span><br><span class="line">	initr_mmc,</span><br><span class="line">	initr_env,</span><br><span class="line">	INIT_FUNC_WATCHDOG_RESET</span><br><span class="line">	initr_secondary_cpu,</span><br><span class="line">	INIT_FUNC_WATCHDOG_RESET</span><br><span class="line">	stdio_add_devices,</span><br><span class="line">	initr_jumptable,</span><br><span class="line">	console_init_r, <span class="comment">/* fully init console as a device */</span></span><br><span class="line">	INIT_FUNC_WATCHDOG_RESET</span><br><span class="line">	interrupt_init,</span><br><span class="line">	initr_enable_interrupts,</span><br><span class="line">	initr_ethaddr,</span><br><span class="line">	board_late_init,</span><br><span class="line">	INIT_FUNC_WATCHDOG_RESET</span><br><span class="line">	INIT_FUNC_WATCHDOG_RESET</span><br><span class="line">	INIT_FUNC_WATCHDOG_RESET</span><br><span class="line">	initr_net,</span><br><span class="line">	INIT_FUNC_WATCHDOG_RESET</span><br><span class="line">	run_main_loop,</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
              </div>
            </details>



<h4 id="8-3-2-重要函数说明"><a href="#8-3-2-重要函数说明" class="headerlink" title="8.3.2 重要函数说明"></a><font size=3>8.3.2 重要函数说明</font></h4><details class="folding-tag" blue><summary> 点击查看重要函数功能分析 </summary>
              <div class='content'>
              <p>第 2 行：initr_trace 函数，如果定义了宏 CONFIG_TRACE 的话就会调用函数 trace_init，初始化和调试跟踪有关的内容。</p><p>第 3 行：initr_reloc 函数用于设置 gd-&gt;flags，标记重定位完成。</p><p>第 4 行：initr_caches 函数用于初始化 cache，使能 cache。</p><p>第 5 行：initr_reloc_global_data 函数，初始化重定位后 gd 的一些成员变量。</p><p>第 6 行：initr_barrier 函数， I.MX6ULL 未用到。</p><p>第 7 行：initr_malloc 函数，初始化 malloc。</p><p>第 8 行：initr_console_record 函数，初始化控制台相关的内容， I.MX6ULL 未用到，空函数。</p><p>第 9 行： bootstage_relocate 函数，启动状态重定位。</p><p>第 10 行：initr_bootstage 函数，初始化 bootstage 什么的。</p><p>第 11 行：board_init 函数，板级初始化，包括 74XX 芯片， I2C、 FEC、 USB 和 QSPI 等。这里执行的是 mx6ull_alientek_emmc.c 文件中的 board_init 函数。</p><p>第 12 行：stdio_init_tables 函数， stdio 相关初始化。</p><p>第 13 行：initr_serial 函数，初始化串口。</p><p>第 14 行：initr_announce 函数，与调试有关，通知已经在 RAM 中运行。</p><p>第 18 行：power_init_board 函数，初始化电源芯片，正点原子的 I.MX6ULL 开发板没有用到。</p><p>第 19 行：initr_flash 函数，对于 I.MX6ULL 而言，没有定义宏 CONFIG_SYS_NO_FLASH的话函数 initr_flash 才有效。但是  mx6_common.h 中定义了宏 CONFIG_SYS_NO_FLASH，所以此函数无效。</p><p>第 21 行：initr_nand 函数，初始化 NAND，如果使用 NAND 版本核心板的话就会初始化NAND。</p><p>第 22 行：initr_mmc 函数，初始化 EMMC，如果使用 EMMC 版本核心板的话就会初始化EMMC，串口输出如下图所示信息：</p><img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV05-uboot%E4%B8%8E%E5%86%85%E6%A0%B8/LV05-01-uboot-04-uboot%E5%90%AF%E5%8A%A8%E6%B5%81%E7%A8%8B/img/image-20221016083509867.png" alt="image-20221016083509867" style="zoom:50%;" /><p>第 23 行：initr_env 函数，初始化环境变量。</p><p>第 25 行：initr_secondary_cpu 函数，初始化其他 CPU 核， I.MX6ULL 只有一个核，所以此函数没用。  </p><p>第 27 行：stdio_add_devices 函数，各种输入输出设备的初始化，如 LCD driver， I.MX6ULL 使用 drv_video_init 函数初始化 LCD。会输出如图所示信息：</p><img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV05-uboot%E4%B8%8E%E5%86%85%E6%A0%B8/LV05-01-uboot-04-uboot%E5%90%AF%E5%8A%A8%E6%B5%81%E7%A8%8B/img/image-20221016083629656.png" alt="image-20221016083629656" style="zoom:50%;" /><p>第 28 行：initr_jumptable 函数，初始化跳转表。  </p><p>第 29 行：console_init_r 函数，控制台初始化，初始化完成以后此函数会调用 stdio_print_current_devices 函数来打印出当前的控制台设备，如图所示：  </p><img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV05-uboot%E4%B8%8E%E5%86%85%E6%A0%B8/LV05-01-uboot-04-uboot%E5%90%AF%E5%8A%A8%E6%B5%81%E7%A8%8B/img/image-20221016083741446.png" alt="image-20221016083741446" style="zoom:50%;" /><p>第 31 行：interrupt_init 函数，初始化中断。</p><p>第 32 行：initr_enable_interrupts 函数，使能中断。</p><p>第 33 行：initr_ethaddr 函数，初始化网络地址，也就是获取 MAC 地址。读取环境变量“ethaddr”的值。  </p><p>第 34 行， board_late_init 函数，板子后续初始化，此函数定义在文件 mx6ull_alientek_emmc.c 中，如果环境变量存储在 EMMC 或者 SD 卡中的话，此函数会调用 board_late_mmc_env_init 函数初始化 EMMC&#x2F;SD。会切换到正在时候用的 emmc 设备。</p><details class="folding-tag" blue><summary> 点击查看 board_late_mmc_env_init </summary>              <div class='content'>              <p>这个函数出现在uboot源码的这个文件中：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">board/freescale/mx6ull_alientek_emmc/mx6ull_alientek_emmc.c # 在该文件被调用</span><br><span class="line">board/freescale/common/mmc.c                                # 在该文件中定义</span><br></pre></td></tr></table></figure><p>函数定义如下：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">void</span> <span class="title function_">board_late_mmc_env_init</span><span class="params">(<span class="type">void</span>)</span></span><br><span class="line">&#123;</span><br><span class="line">	<span class="type">char</span> cmd[<span class="number">32</span>];</span><br><span class="line">	<span class="type">char</span> mmcblk[<span class="number">32</span>];</span><br><span class="line">	u32 dev_no = mmc_get_env_dev();</span><br><span class="line"></span><br><span class="line">	<span class="keyword">if</span> (!check_mmc_autodetect())</span><br><span class="line">		<span class="keyword">return</span>;</span><br><span class="line"></span><br><span class="line">	setenv_ulong(<span class="string">&quot;mmcdev&quot;</span>, dev_no);</span><br><span class="line"></span><br><span class="line">	<span class="comment">/* Set mmcblk env */</span></span><br><span class="line">	<span class="built_in">sprintf</span>(mmcblk, <span class="string">&quot;/dev/mmcblk%dp2 rootwait rw&quot;</span>,</span><br><span class="line">		mmc_map_to_kernel_blk(dev_no));</span><br><span class="line">	setenv(<span class="string">&quot;mmcroot&quot;</span>, mmcblk);</span><br><span class="line"></span><br><span class="line">	<span class="built_in">sprintf</span>(cmd, <span class="string">&quot;mmc dev %d&quot;</span>, dev_no);</span><br><span class="line">	run_command(cmd, <span class="number">0</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>第 17 ~ 18 行（第 46 ~ 47 行）就是运行“mmc dev xx”命令，用于切换到正在使用的EMMC 设备，串口输出信息如图所示：</p><img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV05-uboot%E4%B8%8E%E5%86%85%E6%A0%B8/LV05-01-uboot-04-uboot%E5%90%AF%E5%8A%A8%E6%B5%81%E7%A8%8B/img/image-20221016084737899.png" alt="image-20221016084737899" style="zoom:50%;" />              </div>            </details><p>第 38 行：initr_net  函数，用于初始化网络设备，函数的调用顺序为：initr_net&rarr;eth_initialize&rarr;board_eth_init() ，最终在串口上会输出如下信息：</p><img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV05-uboot%E4%B8%8E%E5%86%85%E6%A0%B8/LV05-01-uboot-04-uboot%E5%90%AF%E5%8A%A8%E6%B5%81%E7%A8%8B/img/image-20221016084922548.png" alt="image-20221016084922548" style="zoom:50%;" /><p>第 40 行， run_main_loop 函数（后边再详细说明），主循环，处理命令。</p>
              </div>
            </details>

<h2 id="9-run-main-loop-函数"><a href="#9-run-main-loop-函数" class="headerlink" title="9. run_main_loop 函数"></a><font size=3>9. run_main_loop 函数</font></h2><p>前边我们分析到了 board_init_r 函数，在此函数中又会调用到 run_main_loop 函数，接下来我们来了解一下这个函数的功能。</p>
<p>uboot 启动以后会进入 bootdelay 秒倒计时，如果在 bootdelay  秒倒计时结束之前按下按下回车键，那么就会进入 uboot 的命令模式，如果倒计时结束以后都没有按下回车键，那么就会自动启动 Linux 内核，这个功能就是由 run_main_loop 函数来完成的。  </p>
<h3 id="9-1-run-main-loop-函数在哪？"><a href="#9-1-run-main-loop-函数在哪？" class="headerlink" title="9.1 run_main_loop 函数在哪？"></a><font size=3>9.1 run_main_loop 函数在哪？</font></h3><p>run_main_loop 函数定义在uboot 源码的这个文件中：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">common/board_r.c</span><br></pre></td></tr></table></figure>

<p>函数内容如下：  </p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">static</span> <span class="type">int</span> <span class="title function_">run_main_loop</span><span class="params">(<span class="type">void</span>)</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="meta">#<span class="keyword">ifdef</span> CONFIG_SANDBOX</span></span><br><span class="line">	sandbox_main_loop_init();</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line">	<span class="comment">/* main_loop() can return to retry autoboot, if so just run it again */</span></span><br><span class="line">	<span class="keyword">for</span> (;;)</span><br><span class="line">		main_loop();</span><br><span class="line">	<span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="9-2-做了什么？"><a href="#9-2-做了什么？" class="headerlink" title="9.2 做了什么？"></a><font size=3>9.2 做了什么？</font></h3><h4 id="9-2-1-run-main-loop函数定义"><a href="#9-2-1-run-main-loop函数定义" class="headerlink" title="9.2.1 run_main_loop函数定义"></a><font size=3>9.2.1 run_main_loop函数定义</font></h4><p>我们按照下边的行号分析：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">static</span> <span class="type">int</span> <span class="title function_">run_main_loop</span><span class="params">(<span class="type">void</span>)</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="meta">#<span class="keyword">ifdef</span> CONFIG_SANDBOX</span></span><br><span class="line">	sandbox_main_loop_init();</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line">	<span class="comment">/* main_loop() can return to retry autoboot, if so just run it again */</span></span><br><span class="line">	<span class="keyword">for</span> (;;)</span><br><span class="line">		main_loop();</span><br><span class="line">	<span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>第 7 行 和第 8 行（第 759 行和第 760 行）是个死循环，“for(;;)”和“while(1)”功能一样，死循环里面就一个 main_loop 函数 。</p>
<h4 id="9-2-2-main-loop-函数"><a href="#9-2-2-main-loop-函数" class="headerlink" title="9.2.2 main_loop 函数"></a><font size=3>9.2.2 main_loop 函数</font></h4><p>这个函数定义在 common&#x2F;main.c  中，函数内容如下：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* We come here after U-Boot is initialised and ready to process commands */</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">main_loop</span><span class="params">(<span class="type">void</span>)</span></span><br><span class="line">&#123;</span><br><span class="line">	<span class="type">const</span> <span class="type">char</span> *s;</span><br><span class="line"></span><br><span class="line">	bootstage_mark_name(BOOTSTAGE_ID_MAIN_LOOP, <span class="string">&quot;main_loop&quot;</span>);</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">ifndef</span> CONFIG_SYS_GENERIC_BOARD</span></span><br><span class="line">	<span class="built_in">puts</span>(<span class="string">&quot;Warning: Your board does not use generic board. Please read\n&quot;</span>);</span><br><span class="line">	<span class="built_in">puts</span>(<span class="string">&quot;doc/README.generic-board and take action. Boards not\n&quot;</span>);</span><br><span class="line">	<span class="built_in">puts</span>(<span class="string">&quot;upgraded by the late 2014 may break or be removed.\n&quot;</span>);</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">ifdef</span> CONFIG_VERSION_VARIABLE</span></span><br><span class="line">	setenv(<span class="string">&quot;ver&quot;</span>, version_string);  <span class="comment">/* set version variable */</span></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span> <span class="comment">/* CONFIG_VERSION_VARIABLE */</span></span></span><br><span class="line"></span><br><span class="line">	cli_init();</span><br><span class="line"></span><br><span class="line">	run_preboot_environment_command();</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">if</span> defined(CONFIG_UPDATE_TFTP)</span></span><br><span class="line">	update_tftp(<span class="number">0UL</span>, <span class="literal">NULL</span>, <span class="literal">NULL</span>);</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span> <span class="comment">/* CONFIG_UPDATE_TFTP */</span></span></span><br><span class="line"></span><br><span class="line">	s = bootdelay_process();</span><br><span class="line">	<span class="keyword">if</span> (cli_process_fdt(&amp;s))</span><br><span class="line">		cli_secure_boot_cmd(s);</span><br><span class="line"></span><br><span class="line">	autoboot_command(s);</span><br><span class="line"></span><br><span class="line">	cli_loop();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>第 6 行（第 48 行）：调用 bootstage_mark_name 函数，打印出启动进度。  </p>
<p>第 15 行（第 57 行）：如果定义了宏 CONFIG_VERSION_VARIABLE 的话就会执行函数 setenv，将变量 ver 的值设置为 version_string，也就是设置版本号环境变量。</p>
<details class="folding-tag" blue><summary> 点击查看 version_string </summary>
              <div class='content'>
              <p>version_string 定义在文件 cmd&#x2F;version.c 中，定义如下：  </p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">const</span> <span class="type">char</span> __weak version_string[] = U_BOOT_VERSION_STRING;</span><br></pre></td></tr></table></figure><p>其中U_BOOT_VERSION_STRING 是个宏， 定义在文件 include&#x2F;version.h：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">define</span> U_BOOT_VERSION_STRING U_BOOT_VERSION <span class="string">&quot; (&quot;</span> U_BOOT_DATE <span class="string">&quot; - &quot;</span> \</span></span><br><span class="line"><span class="meta">	U_BOOT_TIME <span class="string">&quot; &quot;</span> U_BOOT_TZ <span class="string">&quot;)&quot;</span> CONFIG_IDENT_STRING</span></span><br></pre></td></tr></table></figure><p>U_BOOT_VERSION 定义在文件 include&#x2F;generated&#x2F;version_autogenerated.h 中：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">define</span> PLAIN_VERSION <span class="string">&quot;2016.03-g9e33bde-dirty&quot;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> U_BOOT_VERSION <span class="string">&quot;U-Boot &quot;</span> PLAIN_VERSION</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> CC_VERSION_STRING <span class="string">&quot;arm-linux-gnueabihf-gcc (Linaro GCC 4.9-2017.01) 4.9.4&quot;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> LD_VERSION_STRING <span class="string">&quot;GNU ld (Linaro_Binutils-2017.01) 2.24.0.20141017 Linaro 2014_11-3-git&quot;</span></span></span><br></pre></td></tr></table></figure><p>  可以看出， U_BOOT_VERSION 为“U-boot 2016.03”，U_BOOT_DATE 、 U_BOOT_TIME 和 U_BOOT_TZ 定义在 include&#x2F;generated&#x2F;timestamp_autogenerated.h 文件中，内容如下：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">define</span> U_BOOT_DATE <span class="string">&quot;Oct 15 2022&quot;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> U_BOOT_TIME <span class="string">&quot;17:13:29&quot;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> U_BOOT_TZ <span class="string">&quot;+0800&quot;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> U_BOOT_DMI_DATE <span class="string">&quot;10/15/2022&quot;</span></span></span><br></pre></td></tr></table></figure><p>宏 CONFIG_IDENT_STRING 为空，所以 U_BOOT_VERSION_STRING 为“U-Boot 2016.03(Oct 15 2022 - 17:13:29 +0800)”，进入 uboot 命令模式，输入命令“version”查看版本号，如图:</p><img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV05-uboot%E4%B8%8E%E5%86%85%E6%A0%B8/LV05-01-uboot-04-uboot%E5%90%AF%E5%8A%A8%E6%B5%81%E7%A8%8B/img/image-20221016100037636.png" alt="image-20221016100037636" style="zoom:50%;" />
              </div>
            </details>

<p>第 18 行（第 60 行）：cli_init 函数，跟命令初始化有关，初始化 hush shell 相关的变量。</p>
<p>第 20 行（第 62 行）：run_preboot_environment_command 函数，获取环境变量 perboot 的内容， perboot 是一些预启动命令，一般不使用这个环境变量。 </p>
<p>第 26 行（第 68 行）：bootdelay_process 函数，此函数会读取环境变量 bootdelay 和 bootcmd 的内容，然后将 bootdelay 的值赋值给全局变量 stored_bootdelay，返回值为环境变量 bootcmd 的值。</p>
<p>第 27 行（第 69 行）：如果定义了 CONFIG_OF_CONTROL 的话函数 cli_process_fdt 就会实现，如果没有定义 CONFIG_OF_CONTROL 的话函数 cli_process_fdt 直接返回一个 false。在我们使用的 uboot 中没有定义 CONFIG_OF_CONTROL，因此 cli_process_fdt 函数返回值为 false。  </p>
<p>第 30 行（第 72 行）：autoboot_command 函数，此函数就是检查倒计时是否结束，倒计时结束之前有没有被打断。  </p>
<details class="folding-tag" blue><summary> 点击查看 autoboot_command </summary>
              <div class='content'>
              <p>此函数定义在文件 common&#x2F;autoboot.c 中，内容如下：  </p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">void</span> <span class="title function_">autoboot_command</span><span class="params">(<span class="type">const</span> <span class="type">char</span> *s)</span></span><br><span class="line">&#123;</span><br><span class="line">	debug(<span class="string">&quot;### main_loop: bootcmd=\&quot;%s\&quot;\n&quot;</span>, s ? s : <span class="string">&quot;&lt;UNDEFINED&gt;&quot;</span>);</span><br><span class="line"></span><br><span class="line">	<span class="keyword">if</span> (stored_bootdelay != <span class="number">-1</span> &amp;&amp; s &amp;&amp; !abortboot(stored_bootdelay)) &#123;</span><br><span class="line"><span class="meta">#<span class="keyword">if</span> defined(CONFIG_AUTOBOOT_KEYED) &amp;&amp; !defined(CONFIG_AUTOBOOT_KEYED_CTRLC)</span></span><br><span class="line">		<span class="type">int</span> prev = disable_ctrlc(<span class="number">1</span>);	<span class="comment">/* disable Control C checking */</span></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"></span><br><span class="line">		run_command_list(s, <span class="number">-1</span>, <span class="number">0</span>);</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">if</span> defined(CONFIG_AUTOBOOT_KEYED) &amp;&amp; !defined(CONFIG_AUTOBOOT_KEYED_CTRLC)</span></span><br><span class="line">		disable_ctrlc(prev);	<span class="comment">/* restore Control C checking */</span></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">ifdef</span> CONFIG_MENUKEY</span></span><br><span class="line">	<span class="keyword">if</span> (menukey == CONFIG_MENUKEY) &#123;</span><br><span class="line">		s = getenv(<span class="string">&quot;menucmd&quot;</span>);</span><br><span class="line">		<span class="keyword">if</span> (s)</span><br><span class="line">			run_command_list(s, <span class="number">-1</span>, <span class="number">0</span>);</span><br><span class="line">	&#125;</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span> <span class="comment">/* CONFIG_MENUKEY */</span></span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>autoboot_command 函数里面有很多条件编译，宏CONFIG_AUTOBOOT_KEYED 、 CONFIG_AUTOBOOT_KEYED_CTRLC 和<br>CONFIG_MENUKEY 这三个宏在 I.MX6ULL 里面没有定义，去掉条件编译得到如下代码：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">void</span> <span class="title function_">autoboot_command</span><span class="params">(<span class="type">const</span> <span class="type">char</span> *s)</span></span><br><span class="line">&#123;</span><br><span class="line">	<span class="keyword">if</span> (stored_bootdelay != <span class="number">-1</span> &amp;&amp; s &amp;&amp; !abortboot(stored_bootdelay))</span><br><span class="line">	&#123;</span><br><span class="line">		run_command_list(s, <span class="number">-1</span>, <span class="number">0</span>);</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p> 可以看到，当以下三条全部成立的话，就会执行函数 run_command_list ：</p><p>（1）stored_bootdelay 不等于 -1 。</p><p>（2）s 不为空。</p><p>（3）函数 abortboot 返回值为 0。  </p><p>stored_bootdelay 等于环境变量 bootdelay 的值； s 是环境变量 bootcmd 的值，一般不为空，因此前两个成立，就剩下了函数 abortboot 的返回值 ，如果倒计时自然结束那么就执行函数 run_command_list，此函数会执行参数 s 指定的一系列命令，也就是环境变量 bootcmd 的命令，bootcmd 里面保存着默认的启动命令，因此 linux 内核启动！这个就是 uboot 中倒计时结束以后自动启动 linux 内核的原理。如果倒计时结束之前按下了键盘上的按键，那么 run_command_list 函数就不会执行，相当于 autoboot_command 是个空函数。</p><details class="folding-tag" blue><summary> 点击查看 abortboot 函数 </summary>              <div class='content'>              <p> abortboot 函数 也 定 义 在 文 件 common&#x2F;autoboot.c 中：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">static</span> <span class="type">int</span> <span class="title function_">abortboot</span><span class="params">(<span class="type">int</span> bootdelay)</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="meta">#<span class="keyword">ifdef</span> CONFIG_AUTOBOOT_KEYED</span></span><br><span class="line">	<span class="keyword">return</span> abortboot_keyed(bootdelay);</span><br><span class="line"><span class="meta">#<span class="keyword">else</span></span></span><br><span class="line">	<span class="keyword">return</span> abortboot_normal(bootdelay);</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>因为宏 CONFIG_AUTOBOOT_KEYE 未定义，因此执行函数 abortboot_normal ，此函数也定义在文件 common&#x2F;autoboot.c 中，内容<br>如下：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">static</span> <span class="type">int</span> <span class="title function_">abortboot_normal</span><span class="params">(<span class="type">int</span> bootdelay)</span></span><br><span class="line">&#123;</span><br><span class="line">	<span class="type">int</span> <span class="built_in">abort</span> = <span class="number">0</span>;</span><br><span class="line">	<span class="type">unsigned</span> <span class="type">long</span> ts;</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">ifdef</span> CONFIG_MENUPROMPT</span></span><br><span class="line">	<span class="built_in">printf</span>(CONFIG_MENUPROMPT);</span><br><span class="line"><span class="meta">#<span class="keyword">else</span></span></span><br><span class="line">	<span class="keyword">if</span> (bootdelay &gt;= <span class="number">0</span>)</span><br><span class="line">		<span class="built_in">printf</span>(<span class="string">&quot;Hit any key to stop autoboot: %2d &quot;</span>, bootdelay);</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">if</span> defined CONFIG_ZERO_BOOTDELAY_CHECK</span></span><br><span class="line">	<span class="comment">/*</span></span><br><span class="line"><span class="comment">	 * Check if key already pressed</span></span><br><span class="line"><span class="comment">	 * Don&#x27;t check if bootdelay &lt; 0</span></span><br><span class="line"><span class="comment">	 */</span></span><br><span class="line">	<span class="keyword">if</span> (bootdelay &gt;= <span class="number">0</span>) &#123;</span><br><span class="line">		<span class="keyword">if</span> (tstc()) &#123;	<span class="comment">/* we got a key press	*/</span></span><br><span class="line">			(<span class="type">void</span>) getc();  <span class="comment">/* consume input	*/</span></span><br><span class="line">			<span class="built_in">puts</span>(<span class="string">&quot;\b\b\b 0&quot;</span>);</span><br><span class="line">			<span class="built_in">abort</span> = <span class="number">1</span>;	<span class="comment">/* don&#x27;t auto boot	*/</span></span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"></span><br><span class="line">	<span class="keyword">while</span> ((bootdelay &gt; <span class="number">0</span>) &amp;&amp; (!<span class="built_in">abort</span>)) &#123;</span><br><span class="line">		--bootdelay;</span><br><span class="line">		<span class="comment">/* delay 1000 ms */</span></span><br><span class="line">		ts = get_timer(<span class="number">0</span>);</span><br><span class="line">		<span class="keyword">do</span> &#123;</span><br><span class="line">			<span class="keyword">if</span> (tstc()) &#123;	<span class="comment">/* we got a key press	*/</span></span><br><span class="line">				<span class="built_in">abort</span>  = <span class="number">1</span>;	<span class="comment">/* don&#x27;t auto boot	*/</span></span><br><span class="line">				bootdelay = <span class="number">0</span>;	<span class="comment">/* no more delay	*/</span></span><br><span class="line"><span class="meta"># <span class="keyword">ifdef</span> CONFIG_MENUKEY</span></span><br><span class="line">				menukey = getc();</span><br><span class="line"><span class="meta"># <span class="keyword">else</span></span></span><br><span class="line">				(<span class="type">void</span>) getc();  <span class="comment">/* consume input	*/</span></span><br><span class="line"><span class="meta"># <span class="keyword">endif</span></span></span><br><span class="line">				<span class="keyword">break</span>;</span><br><span class="line">			&#125;</span><br><span class="line">			udelay(<span class="number">10000</span>);</span><br><span class="line">		&#125; <span class="keyword">while</span> (!<span class="built_in">abort</span> &amp;&amp; get_timer(ts) &lt; <span class="number">1000</span>);</span><br><span class="line"></span><br><span class="line">		<span class="built_in">printf</span>(<span class="string">&quot;\b\b\b%2d &quot;</span>, bootdelay);</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	putc(<span class="string">&#x27;\n&#x27;</span>);</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">ifdef</span> CONFIG_SILENT_CONSOLE</span></span><br><span class="line">	<span class="keyword">if</span> (<span class="built_in">abort</span>)</span><br><span class="line">		gd-&gt;flags &amp;= ~GD_FLG_SILENT;</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"></span><br><span class="line">	<span class="keyword">return</span> <span class="built_in">abort</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>函数 abortboot_normal 同样很多条件编译，删除掉条件编译相关代码后 abortboot_normal 函数内容如下：  </p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">static</span> <span class="type">int</span> <span class="title function_">abortboot_normal</span><span class="params">(<span class="type">int</span> bootdelay)</span></span><br><span class="line">&#123;</span><br><span class="line">	<span class="type">int</span> <span class="built_in">abort</span> = <span class="number">0</span>;</span><br><span class="line">	<span class="type">unsigned</span> <span class="type">long</span> ts;</span><br><span class="line"></span><br><span class="line">	<span class="keyword">if</span> (bootdelay &gt;= <span class="number">0</span>)</span><br><span class="line">		<span class="built_in">printf</span>(<span class="string">&quot;Hit any key to stop autoboot: %2d &quot;</span>, bootdelay);</span><br><span class="line"></span><br><span class="line">	<span class="keyword">while</span> ((bootdelay &gt; <span class="number">0</span>) &amp;&amp; (!<span class="built_in">abort</span>))</span><br><span class="line">	&#123;</span><br><span class="line">		--bootdelay;</span><br><span class="line">		<span class="comment">/* delay 1000 ms */</span></span><br><span class="line">		ts = get_timer(<span class="number">0</span>);</span><br><span class="line">		<span class="keyword">do</span></span><br><span class="line">		&#123;</span><br><span class="line">			<span class="keyword">if</span> (tstc())   <span class="comment">/* we got a key press */</span></span><br><span class="line">			&#123;</span><br><span class="line">				<span class="built_in">abort</span> = <span class="number">1</span>; <span class="comment">/* don&#x27;t auto boot */</span></span><br><span class="line">				bootdelay = <span class="number">0</span>; <span class="comment">/* no more delay */</span></span><br><span class="line">				(<span class="type">void</span>) getc(); <span class="comment">/* consume input */</span></span><br><span class="line">				<span class="keyword">break</span>;</span><br><span class="line">			&#125;</span><br><span class="line">			udelay(<span class="number">10000</span>);</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">while</span> (!<span class="built_in">abort</span> &amp;&amp; get_timer(ts) &lt; <span class="number">1000</span>);</span><br><span class="line"></span><br><span class="line">		<span class="built_in">printf</span>(<span class="string">&quot;\b\b\b%2d &quot;</span>, bootdelay);</span><br><span class="line">	&#125;</span><br><span class="line">	putc(<span class="string">&#x27;\n&#x27;</span>);</span><br><span class="line">	<span class="keyword">return</span> <span class="built_in">abort</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>第 3 行：变量 abort 是函数 abortboot_normal 的返回值，默认值为 0。</p><p> 第 7 行：通过串口输出“Hit any key to stop autoboot”字样，如图：</p><img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV05-uboot%E4%B8%8E%E5%86%85%E6%A0%B8/LV05-01-uboot-04-uboot%E5%90%AF%E5%8A%A8%E6%B5%81%E7%A8%8B/img/image-20221016101203235.png" alt="image-20221016101203235" style="zoom: 50%;" /><p>第 9 ~ 25 行：是倒计时的具体实现。其中的第 16 行判断键盘是否有按下，也就是是否打断了倒计时，如果键盘按下的话就执行相应的<br>分支。比如设置 abort 为 1，设置 bootdelay 为 0 等，最后跳出倒计时循环。   </p><p>第 30 行，返回 abort 的值，如果倒计时自然结束，没有被打断 abort 就为 0，否则的话 abort的值就为 1。 </p>              </div>            </details> 
              </div>
            </details>

<p>第 32 行（第 74 行）：如果倒计时结束之前按下按键，那么就会执行cli_loop 函数，这个就是命令处理函数，负责接收好处理输入的命令。  </p>
<h2 id="10-cli-loop-函数"><a href="#10-cli-loop-函数" class="headerlink" title="10. cli_loop 函数"></a><font size=3>10. cli_loop 函数</font></h2><p>前边我们分析到，当执行run_main_loop 函数，倒计时结束前按下按键，会执行 cli_loop，接下来就来了解一下这个函数。</p>
<h3 id="10-1-cli-loop-函数在哪？"><a href="#10-1-cli-loop-函数在哪？" class="headerlink" title="10.1 cli_loop 函数在哪？"></a><font size=3>10.1 cli_loop 函数在哪？</font></h3><p>cli_loop 函数是 uboot 的命令行处理函数，最终是调用到 cmd_process 函数来处理命令。我们在 uboot 中输入各种命令，进行各种操作就是 cli_loop 来处理的，此函数定义在uboot源码这个文件中 ：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">common/cli.c</span><br></pre></td></tr></table></figure>

<p>我们打开这个文件，可以看到函数内容如下：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">void</span> <span class="title function_">cli_loop</span><span class="params">(<span class="type">void</span>)</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="meta">#<span class="keyword">ifdef</span> CONFIG_SYS_HUSH_PARSER</span></span><br><span class="line">	parse_file_outer();</span><br><span class="line">	<span class="comment">/* This point is never reached */</span></span><br><span class="line">	<span class="keyword">for</span> (;;);</span><br><span class="line"><span class="meta">#<span class="keyword">else</span></span></span><br><span class="line">	cli_simple_loop();</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span> <span class="comment">/*CONFIG_SYS_HUSH_PARSER*/</span></span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="10-2-做了什么？"><a href="#10-2-做了什么？" class="headerlink" title="10.2 做了什么？"></a><font size=3>10.2 做了什么？</font></h3><h4 id="10-2-1-cli-loop"><a href="#10-2-1-cli-loop" class="headerlink" title="10.2.1 cli_loop"></a><font size=3>10.2.1 cli_loop</font></h4><p>我们按照下边的行号分析：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">void</span> <span class="title function_">cli_loop</span><span class="params">(<span class="type">void</span>)</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="meta">#<span class="keyword">ifdef</span> CONFIG_SYS_HUSH_PARSER</span></span><br><span class="line">	parse_file_outer();</span><br><span class="line">	<span class="comment">/* This point is never reached */</span></span><br><span class="line">	<span class="keyword">for</span> (;;);</span><br><span class="line"><span class="meta">#<span class="keyword">else</span></span></span><br><span class="line">	cli_simple_loop();</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span> <span class="comment">/*CONFIG_SYS_HUSH_PARSER*/</span></span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>在文件 include&#x2F;configs&#x2F;mx6_common.h 中有定义宏 CONFIG_SYS_HUSH_PARSER，而正点原子的 I.MX6ULL 开发板配置头文件 mx6ullevk.h 里面会引用 mx_common.h 这个头文件，因此宏 CONFIG_SYS_HUSH_PARSER 有定义。  </p>
<p>第 4 行（第 205 行）：调用函数 parse_file_outer（10.2.2中分析）。</p>
<p>第 6 行（第 207 行）是个死循环，永远不会执行到这里。  </p>
<h4 id="10-2-2-parse-file-outer-函数"><a href="#10-2-2-parse-file-outer-函数" class="headerlink" title="10.2.2 parse_file_outer 函数"></a><font size=3>10.2.2 parse_file_outer 函数</font></h4><p>函数 parse_file_outer 定义在文件 common&#x2F;cli_hush.c 中，去掉条件编译内容以后的函数内容如下：  </p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="title function_">parse_file_outer</span><span class="params">(<span class="type">void</span>)</span></span><br><span class="line">&#123;</span><br><span class="line">	<span class="type">int</span> rcode;</span><br><span class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">in_str</span> <span class="title">input</span>;</span></span><br><span class="line">	setup_file_in_str(&amp;input);</span><br><span class="line">	rcode = parse_stream_outer(&amp;input, FLAG_PARSE_SEMICOLON);</span><br><span class="line">	<span class="keyword">return</span> rcode;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>第 5 行：调用函数 setup_file_in_str 初始化变量 input 的成员变量。</p>
<p>第 6 行：调用函数 parse_stream_outer（10.2.3中分析），这个函数就是 hush shell 的命令解释器，负责接收命令行输入，然后解析并执行相应的命令。</p>
<h4 id="10-2-3-parse-stream-outer函数"><a href="#10-2-3-parse-stream-outer函数" class="headerlink" title="10.2.3 parse_stream_outer函数"></a><font size=3>10.2.3 parse_stream_outer函数</font></h4><p>函数 parse_stream_outer 定义在文件 common&#x2F;cli_hush.c中，精简版的函数内容如下：  </p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">static</span> <span class="type">int</span> <span class="title function_">parse_stream_outer</span><span class="params">(<span class="keyword">struct</span> in_str *inp, <span class="type">int</span> flag)</span></span><br><span class="line">&#123;</span><br><span class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">p_context</span> <span class="title">ctx</span>;</span></span><br><span class="line">	o_string temp=NULL_O_STRING;</span><br><span class="line">	<span class="type">int</span> rcode;</span><br><span class="line">	<span class="type">int</span> code = <span class="number">1</span>;</span><br><span class="line">	<span class="keyword">do</span></span><br><span class="line">	&#123;</span><br><span class="line">		<span class="comment">// 中间的省略 ... ...</span></span><br><span class="line">		rcode = parse_stream(&amp;temp, &amp;ctx, inp,</span><br><span class="line">		                     flag &amp; FLAG_CONT_ON_NEWLINE ? <span class="number">-1</span> : <span class="string">&#x27;\n&#x27;</span>);</span><br><span class="line">		<span class="comment">// 中间的省略 ......</span></span><br><span class="line">		<span class="keyword">if</span> (rcode != <span class="number">1</span> &amp;&amp; ctx.old_flag == <span class="number">0</span>)</span><br><span class="line">		&#123;</span><br><span class="line">			<span class="comment">// 中间的省略 ......</span></span><br><span class="line">			run_list(ctx.list_head);</span><br><span class="line">			<span class="comment">// 中间的省略 ......</span></span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">else</span></span><br><span class="line">		&#123;</span><br><span class="line">		<span class="comment">// 中间的省略 ......</span></span><br><span class="line">		&#125;</span><br><span class="line">		b_free(&amp;temp);</span><br><span class="line">		<span class="comment">/* loop on syntax errors, return on EOF */</span></span><br><span class="line">	&#125;</span><br><span class="line">	<span class="keyword">while</span> (rcode != <span class="number">-1</span> &amp;&amp; !(flag &amp; FLAG_EXIT_FROM_LOOP) &amp;&amp;</span><br><span class="line">	        (inp-&gt;peek != static_peek || b_peek(inp)));</span><br><span class="line">	<span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>第 7 ~ 21 行：这几行中的do-while 循环就是处理输入命令的。</p>
<p>第 10 行：调用函数 parse_stream 进行命令解析。  </p>
<p>第 16 行：调用 run_list 函数来执行解析出来的命令 。</p>
<h4 id="10-2-4-run-list-函数"><a href="#10-2-4-run-list-函数" class="headerlink" title="10.2.4 run_list 函数"></a><font size=3>10.2.4 run_list 函数</font></h4><p>该函数定义在 common&#x2F;cli_hush.c 文件中，函数会经过一系列的函数调用，最终通过调用 cmd_process 函数来处理命令 ：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* Select which version we will use */</span></span><br><span class="line"><span class="type">static</span> <span class="type">int</span> <span class="title function_">run_list</span><span class="params">(<span class="keyword">struct</span> pipe *pi)</span></span><br><span class="line">&#123;</span><br><span class="line">	<span class="type">int</span> rcode=<span class="number">0</span>;</span><br><span class="line"><span class="meta">#<span class="keyword">ifndef</span> __U_BOOT__</span></span><br><span class="line">	<span class="keyword">if</span> (fake_mode==<span class="number">0</span>) &#123;</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line">		rcode = run_list_real(pi);</span><br><span class="line"><span class="meta">#<span class="keyword">ifndef</span> __U_BOOT__</span></span><br><span class="line">	&#125;</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line">	<span class="comment">/* free_pipe_list has the side effect of clearing memory</span></span><br><span class="line"><span class="comment">	 * In the long run that function can be merged with run_list_real,</span></span><br><span class="line"><span class="comment">	 * but doing that now would hobble the debugging effort. */</span></span><br><span class="line">	free_pipe_list(pi,<span class="number">0</span>);</span><br><span class="line">	<span class="keyword">return</span> rcode;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>第 8 行：run_list 调用 run_list_real 函数。  </p>
<ul>
<li>run_list_real 函数</li>
</ul>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// common/cli_hush.c</span></span><br><span class="line"><span class="type">static</span> <span class="type">int</span> <span class="title function_">run_list_real</span><span class="params">(<span class="keyword">struct</span> pipe *pi)</span></span><br><span class="line">&#123;</span><br><span class="line">	<span class="type">char</span> *save_name = <span class="literal">NULL</span>;</span><br><span class="line">	<span class="comment">// 中间的省略 ... ...</span></span><br><span class="line">	<span class="type">int</span> if_code=<span class="number">0</span>, next_if_code=<span class="number">0</span>;  <span class="comment">/* need double-buffer to handle elif */</span></span><br><span class="line">	<span class="comment">// 中间的省略 ... ...</span></span><br><span class="line">	rcode = run_pipe_real(pi);</span><br><span class="line">	<span class="comment">// 中间的省略 ... ...</span></span><br><span class="line">	<span class="keyword">return</span> rcode;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>第 8 行：run_list_real 函数调用 run_pipe_real 函数。  </p>
<ul>
<li>run_pipe_real 函数</li>
</ul>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// common/cli_hush.c</span></span><br><span class="line"><span class="type">static</span> <span class="type">int</span> <span class="title function_">run_pipe_real</span><span class="params">(<span class="keyword">struct</span> pipe *pi)</span></span><br><span class="line">&#123;</span><br><span class="line">	<span class="type">int</span> i;</span><br><span class="line"></span><br><span class="line">	<span class="type">int</span> nextin;</span><br><span class="line">	<span class="type">int</span> flag = do_repeat ? CMD_FLAG_REPEAT : <span class="number">0</span>;</span><br><span class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">child_prog</span> *<span class="title">child</span>;</span></span><br><span class="line">	<span class="type">char</span> *p;</span><br><span class="line">	<span class="comment">// 中间的省略 ... ...</span></span><br><span class="line">	<span class="keyword">if</span> (pi-&gt;num_progs == <span class="number">1</span>) child = &amp; (pi-&gt;progs[<span class="number">0</span>]);</span><br><span class="line">	<span class="comment">// 中间的省略 ... ...</span></span><br><span class="line">	<span class="keyword">return</span> rcode;</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="keyword">else</span> <span class="keyword">if</span> (pi-&gt;num_progs == <span class="number">1</span> &amp;&amp; pi-&gt;progs[<span class="number">0</span>].argv != <span class="literal">NULL</span>)</span><br><span class="line">	&#123;</span><br><span class="line">		<span class="comment">// 中间的省略 ... ...</span></span><br><span class="line">		<span class="comment">/* Process the command */</span></span><br><span class="line">		<span class="keyword">return</span> cmd_process(flag, child-&gt;argc, child-&gt;argv,</span><br><span class="line">						   &amp;flag_repeat, <span class="literal">NULL</span>);</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>第 19 行：run_pipe_real 函数调用 cmd_process 函数。  </p>
<h2 id="11-cmd-process-函数"><a href="#11-cmd-process-函数" class="headerlink" title="11. cmd_process 函数"></a><font size=3>11. cmd_process 函数</font></h2><p>我们在uboot中使用的命令，是通过该函数进行解析和执行的。</p>
<h3 id="11-1-命令的定义与执行"><a href="#11-1-命令的定义与执行" class="headerlink" title="11.1 命令的定义与执行"></a><font size=3>11.1 命令的定义与执行</font></h3><h4 id="11-1-1-相关宏定义"><a href="#11-1-1-相关宏定义" class="headerlink" title="11.1.1 相关宏定义"></a><font size=3>11.1.1 相关宏定义</font></h4><p>uboot使用宏U_BOOT_CMD来定义命令，宏 U_BOOT_CMD 定义在文件 include&#x2F;command.h 中，定义如下：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">define</span> U_BOOT_CMD(_name, _maxargs, _rep, _cmd, _usage, _help)		\</span></span><br><span class="line"><span class="meta">	U_BOOT_CMD_COMPLETE(_name, _maxargs, _rep, _cmd, _usage, _help, NULL)</span></span><br></pre></td></tr></table></figure>

<p>可以看出 U_BOOT_CMD 是 U_BOOT_CMD_COMPLETE 的 特 例 ， 我们将U_BOOT_CMD_COMPLETE 的最后一个参数设置成NULL 就是 U_BOOT_CMD。U_BOOT_CMD_COMPLETE 定义在文件 include&#x2F;command.h 中，定义如下：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">define</span> U_BOOT_CMD_COMPLETE(_name, _maxargs, _rep, _cmd, _usage, _help, _comp) \</span></span><br><span class="line"><span class="meta">	ll_entry_declare(cmd_tbl_t, _name, cmd) =			\</span></span><br><span class="line"><span class="meta">		U_BOOT_CMD_MKENT_COMPLETE(_name, _maxargs, _rep, _cmd,	\</span></span><br><span class="line"><span class="meta">						_usage, _help, _comp);</span></span><br></pre></td></tr></table></figure>

<p>宏 U_BOOT_CMD_COMPLETE 又 用 到 了 ll_entry_declare 和 U_BOOT_CMD_MKENT_COMPLETE。 ll_entry_declar 定义在文件include&#x2F;linker_lists.h 中，定义如下：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">define</span> ll_entry_declare(_type, _name, _list)				\</span></span><br><span class="line"><span class="meta">	_type _u_boot_list_2_##_list##_2_##_name __aligned(4)		\</span></span><br><span class="line"><span class="meta">			__attribute__((unused,				\</span></span><br><span class="line"><span class="meta">			section(<span class="string">&quot;.u_boot_list_2_&quot;</span>#_list<span class="string">&quot;_2_&quot;</span>#_name)))</span></span><br></pre></td></tr></table></figure>

<p>其中_type 为 cmd_tbl_t，因此 ll_entry_declare 就是定义了一个 cmd_tbl_t 变量，这里用到了 C 语言中的“##”连接符。其中的“##_list”表示用_list 的值来替换，“##_name”就是用_name 的值来替换。  </p>
<p>宏 U_BOOT_CMD_MKENT_COMPLETE 定义在文件 include&#x2F;command.h 中，内容如下：  </p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">define</span> U_BOOT_CMD_MKENT_COMPLETE(_name, _maxargs, _rep, _cmd,		\</span></span><br><span class="line"><span class="meta">				_usage, _help, _comp)			\</span></span><br><span class="line"><span class="meta">		&#123; #_name, _maxargs, _rep, _cmd, _usage,			\</span></span><br><span class="line"><span class="meta">			_CMD_HELP(_help) _CMD_COMPLETE(_comp) &#125;</span></span><br></pre></td></tr></table></figure>

<p>“ # ” 表 示 将 _name 传递过来的值字符串化 ，U_BOOT_CMD_MKENT_COMPLETE 又用到了宏_CMD_HELP 和_CMD_COMPLETE，这两个宏的定义（include&#x2F;command.h）如下：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">ifdef</span> CONFIG_AUTO_COMPLETE</span></span><br><span class="line"><span class="meta"># <span class="keyword">define</span> _CMD_COMPLETE(x) x,</span></span><br><span class="line"><span class="meta">#<span class="keyword">else</span></span></span><br><span class="line"><span class="meta"># <span class="keyword">define</span> _CMD_COMPLETE(x)</span></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">ifdef</span> CONFIG_SYS_LONGHELP</span></span><br><span class="line"><span class="meta"># <span class="keyword">define</span> _CMD_HELP(x) x,</span></span><br><span class="line"><span class="meta">#<span class="keyword">else</span></span></span><br><span class="line"><span class="meta"># <span class="keyword">define</span> _CMD_HELP(x)</span></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br></pre></td></tr></table></figure>

<p>可以看出，如果定义了宏 CONFIG_AUTO_COMPLETE 和 CONFIG_SYS_LONGHELP 的话 ， _CMD_COMPLETE 和 _CMD_HELP 就是取自身的值，然后在加上一个 ‘ , ’。  CONFIG_AUTO_COMPLETE 和 CONFIG_SYS_LONGHELP 这两个宏有定义在文件 mx6_common.h 中。  </p>
<h4 id="11-1-2-命令实例"><a href="#11-1-2-命令实例" class="headerlink" title="11.1.2 命令实例"></a><font size=3>11.1.2 命令实例</font></h4><p>U_BOOT_CMD宏的流程大概我们已经清楚了，我们就以一个具体的命令为例，来看一下 U_BOOT_CMD 经过展开以后究竟是个什么模样的。以命令 dhcp 为例， dhcp 命令定义在 cmd&#x2F;net.c 文件中：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">U_BOOT_CMD(</span><br><span class="line">	dhcp,	<span class="number">3</span>,	<span class="number">1</span>,	do_dhcp,</span><br><span class="line">	<span class="string">&quot;boot image via network using DHCP/TFTP protocol&quot;</span>,</span><br><span class="line">	<span class="string">&quot;[loadAddress] [[hostIPaddr:]bootfilename]&quot;</span></span><br><span class="line">);</span><br></pre></td></tr></table></figure>

<p>  我们将这个命令进行一个展开：</p>
<ul>
<li>将 U_BOOT_CMD 展开</li>
</ul>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">U_BOOT_CMD_COMPLETE(dhcp, <span class="number">3</span>, <span class="number">1</span>, do_dhcp,</span><br><span class="line">                    <span class="string">&quot;boot image via network using DHCP/TFTP protocol&quot;</span>,</span><br><span class="line">                    <span class="string">&quot;[loadAddress] [[hostIPaddr:]bootfilename]&quot;</span>,</span><br><span class="line">                    <span class="literal">NULL</span>);</span><br></pre></td></tr></table></figure>

<ul>
<li>将 U_BOOT_CMD_COMPLETE 展开</li>
</ul>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">ll_entry_declare(<span class="type">cmd_tbl_t</span>, dhcp, cmd) = \</span><br><span class="line">        U_BOOT_CMD_MKENT_COMPLETE(dhcp, <span class="number">3</span>, <span class="number">1</span>, do_dhcp, \</span><br><span class="line">                                  <span class="string">&quot;boot image via network using DHCP/TFTP protocol&quot;</span>, \</span><br><span class="line">                                  <span class="string">&quot;[loadAddress] [[hostIPaddr:]bootfilename]&quot;</span>, \</span><br><span class="line">                                  <span class="literal">NULL</span>);</span><br></pre></td></tr></table></figure>

<ul>
<li>将 ll_entry_declare 和 U_BOOT_CMD_MKENT_COMPLETE 展开</li>
</ul>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">cmd_tbl_t</span> _u_boot_list_2_cmd_2_dhcp __aligned(<span class="number">4</span>) \</span><br><span class="line">__attribute__((unused,section(.u_boot_list_2_cmd_2_dhcp))) \</span><br><span class="line">&#123;</span><br><span class="line">	<span class="string">&quot;dhcp&quot;</span>, <span class="number">3</span>, <span class="number">1</span>, do_dhcp, \</span><br><span class="line">	<span class="string">&quot;boot image via network using DHCP/TFTP protocol&quot;</span>, \</span><br><span class="line">	<span class="string">&quot;[loadAddress] [[hostIPaddr:]bootfilename]&quot;</span>,\</span><br><span class="line">	<span class="literal">NULL</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>所以，dhcp 命令展开后，最终形式为：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">cmd_tbl_t</span> _u_boot_list_2_cmd_2_dhcp __aligned(<span class="number">4</span>) \</span><br><span class="line">__attribute__((unused,section(.u_boot_list_2_cmd_2_dhcp))) \</span><br><span class="line">&#123;</span><br><span class="line">	<span class="string">&quot;dhcp&quot;</span>, <span class="number">3</span>, <span class="number">1</span>, do_dhcp, \</span><br><span class="line">	<span class="string">&quot;boot image via network using DHCP/TFTP protocol&quot;</span>, \</span><br><span class="line">	<span class="string">&quot;[loadAddress] [[hostIPaddr:]bootfilename]&quot;</span>,\</span><br><span class="line">	<span class="literal">NULL</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>第 1 行：定义了一个 cmd_tbl_t 类型的变量，变量名为_u_boot_list_2_cmd_2_dhcp，此变量 4 字节对齐。  </p>
<p>第 2 行 ：使 用 __attribute__ 关键字设置变量 _u_boot_list_2_cmd_2_dhcp 存储在 .u_boot_list_2_cmd_2_dhcp 段中。u-boot.lds 链接脚本中有一个名为“.u_boot_list”的段，所有.u_boot_list 开头的段都存放到.u_boot.list 中，所以第 2 行 就是设置变量_u_boot_list_2_cmd_2_dhcp 的存储位置。  </p>
<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV05-uboot%E4%B8%8E%E5%86%85%E6%A0%B8/LV05-01-uboot-04-uboot%E5%90%AF%E5%8A%A8%E6%B5%81%E7%A8%8B/img/image-20221016113057647.png" alt="image-20221016113057647" style="zoom: 50%;" />

<p>第 3~6 行： cmd_tbl_t 是个结构体，因此第 3 ~ 6 行是初始化 cmd_tbl_t 这个结构体的各个成员变量。</p>
<details class="folding-tag" blue><summary> 点击查看 cmd_tbl_t 结构体成员 </summary>
              <div class='content'>
              <p>cmd_tbl_t 结构体定义在文件 include&#x2F;command.h 中 ：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">cmd_tbl_s</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">	<span class="type">char</span>	*name;		<span class="comment">/* Command Name			*/</span></span><br><span class="line">	<span class="type">int</span>		maxargs;	<span class="comment">/* maximum number of arguments	*/</span></span><br><span class="line">	<span class="type">int</span>		repeatable;	<span class="comment">/* autorepeat allowed?		*/</span></span><br><span class="line">	<span class="comment">/* Implementation function	*/</span></span><br><span class="line">	<span class="type">int</span>		(*cmd)(<span class="keyword">struct</span> cmd_tbl_s *, <span class="type">int</span>, <span class="type">int</span>, <span class="type">char</span> * <span class="type">const</span> []);</span><br><span class="line">	<span class="type">char</span>	*usage;		<span class="comment">/* Usage message	(short)	*/</span></span><br><span class="line"><span class="meta">#<span class="keyword">ifdef</span>	CONFIG_SYS_LONGHELP</span></span><br><span class="line">	<span class="type">char</span>	*help;		<span class="comment">/* Help  message	(long)	*/</span></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">ifdef</span> CONFIG_AUTO_COMPLETE</span></span><br><span class="line">	<span class="comment">/* do auto completion on the arguments */</span></span><br><span class="line">	<span class="type">int</span>		(*complete)(<span class="type">int</span> argc, <span class="type">char</span> * <span class="type">const</span> argv[], <span class="type">char</span> last_char, <span class="type">int</span> maxv, <span class="type">char</span> *cmdv[]);</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> <span class="title">cmd_tbl_s</span>	<span class="title">cmd_tbl_t</span>;</span></span><br></pre></td></tr></table></figure><p>结合 dhcp 命令展开后的最终形式，可以得出变量_u_boot_list_2_cmd_2_dhcp 的各个成员的值如下所示：  </p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">_u_boot_list_2_cmd_2_dhcp.name = <span class="string">&quot;dhcp&quot;</span></span><br><span class="line">_u_boot_list_2_cmd_2_dhcp.maxargs = <span class="number">3</span></span><br><span class="line">_u_boot_list_2_cmd_2_dhcp.repeatable = <span class="number">1</span></span><br><span class="line">_u_boot_list_2_cmd_2_dhcp.cmd = do_dhcp</span><br><span class="line">_u_boot_list_2_cmd_2_dhcp.usage = <span class="string">&quot;boot image via network using DHCP/TFTP protocol&quot;</span></span><br><span class="line">_u_boot_list_2_cmd_2_dhcp.help = <span class="string">&quot;[loadAddress] [[hostIPaddr:]bootfilename]&quot;</span></span><br><span class="line">_u_boot_list_2_cmd_2_dhcp.complete = <span class="literal">NULL</span></span><br></pre></td></tr></table></figure>
              </div>
            </details>

<h4 id="11-2-3-命令执行"><a href="#11-2-3-命令执行" class="headerlink" title="11.2.3 命令执行"></a><font size=3>11.2.3 命令执行</font></h4><p>前边我们已经分析了在uboot中是如何定义一个命令的，那当我们在 uboot 的命令行中输入“dhcp”这个命令的时候，是怎么执行的呢？其实最终执行的是 do_dhcp 这个函数。</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">static</span> <span class="type">int</span> <span class="title function_">do_dhcp</span><span class="params">(<span class="type">cmd_tbl_t</span> *cmdtp, <span class="type">int</span> flag, <span class="type">int</span> argc, <span class="type">char</span> * <span class="type">const</span> argv[])</span></span><br><span class="line">&#123;</span><br><span class="line">	<span class="keyword">return</span> netboot_common(DHCP, cmdtp, argc, argv);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>在这个函数中实际上是调用了 netboot_common 函数，这个函数定义在 cmd&#x2F;net.c 中，具体怎么执行的，这里我就没有去了解了。</p>
<p>总的来说，uboot 中使用 U_BOOT_CMD 来定义一个命令，最终的目的就是为了定义一个 cmd_tbl_t 类型的变量，并初始化这个变量的各个成员。 uboot 中的每个命令都存储在.u_boot_list 段中，每个命令都有一个名为 do_xxx(xxx 为具体的命令名)的函数，这个 do_xxx 函数就是具体的命令处理函数。  </p>
<h3 id="11-2-cmd-process-函数在哪？"><a href="#11-2-cmd-process-函数在哪？" class="headerlink" title="11.2 cmd_process 函数在哪？"></a><font size=3>11.2 cmd_process 函数在哪？</font></h3><p>cmd_process 函数定义在文件 common&#x2F;command.c 中，函数内容如下： </p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">enum</span> <span class="type">command_ret_t</span> <span class="title function_">cmd_process</span><span class="params">(<span class="type">int</span> flag, <span class="type">int</span> argc, <span class="type">char</span> * <span class="type">const</span> argv[],</span></span><br><span class="line"><span class="params">			       <span class="type">int</span> *repeatable, ulong *ticks)</span></span><br><span class="line">&#123;</span><br><span class="line">	<span class="class"><span class="keyword">enum</span> <span class="title">command_ret_t</span> <span class="title">rc</span> =</span> CMD_RET_SUCCESS;</span><br><span class="line">	<span class="type">cmd_tbl_t</span> *cmdtp;</span><br><span class="line"></span><br><span class="line">	<span class="comment">/* Look up command in command table */</span></span><br><span class="line">	cmdtp = find_cmd(argv[<span class="number">0</span>]);</span><br><span class="line">	<span class="keyword">if</span> (cmdtp == <span class="literal">NULL</span>) &#123;</span><br><span class="line">		<span class="built_in">printf</span>(<span class="string">&quot;Unknown command &#x27;%s&#x27; - try &#x27;help&#x27;\n&quot;</span>, argv[<span class="number">0</span>]);</span><br><span class="line">		<span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="comment">/* found - check max args */</span></span><br><span class="line">	<span class="keyword">if</span> (argc &gt; cmdtp-&gt;maxargs)</span><br><span class="line">		rc = CMD_RET_USAGE;</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">if</span> defined(CONFIG_CMD_BOOTD)</span></span><br><span class="line">	<span class="comment">/* avoid &quot;bootd&quot; recursion */</span></span><br><span class="line">	<span class="keyword">else</span> <span class="keyword">if</span> (cmdtp-&gt;cmd == do_bootd) &#123;</span><br><span class="line">		<span class="keyword">if</span> (flag &amp; CMD_FLAG_BOOTD) &#123;</span><br><span class="line">			<span class="built_in">puts</span>(<span class="string">&quot;&#x27;bootd&#x27; recursion detected\n&quot;</span>);</span><br><span class="line">			rc = CMD_RET_FAILURE;</span><br><span class="line">		&#125; <span class="keyword">else</span> &#123;</span><br><span class="line">			flag |= CMD_FLAG_BOOTD;</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"></span><br><span class="line">	<span class="comment">/* If OK so far, then do the command */</span></span><br><span class="line">	<span class="keyword">if</span> (!rc) &#123;</span><br><span class="line">		<span class="keyword">if</span> (ticks)</span><br><span class="line">			*ticks = get_timer(<span class="number">0</span>);</span><br><span class="line">		rc = cmd_call(cmdtp, flag, argc, argv);</span><br><span class="line">		<span class="keyword">if</span> (ticks)</span><br><span class="line">			*ticks = get_timer(*ticks);</span><br><span class="line">		*repeatable &amp;= cmdtp-&gt;repeatable;</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="keyword">if</span> (rc == CMD_RET_USAGE)</span><br><span class="line">		rc = cmd_usage(cmdtp);</span><br><span class="line">	<span class="keyword">return</span> rc;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="11-3-做了什么？"><a href="#11-3-做了什么？" class="headerlink" title="11.3 做了什么？"></a><font size=3>11.3 做了什么？</font></h3><p>我们按照下边的行号分析：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">enum</span> <span class="type">command_ret_t</span> <span class="title function_">cmd_process</span><span class="params">(<span class="type">int</span> flag, <span class="type">int</span> argc, <span class="type">char</span> * <span class="type">const</span> argv[],</span></span><br><span class="line"><span class="params">			       <span class="type">int</span> *repeatable, ulong *ticks)</span></span><br><span class="line">&#123;</span><br><span class="line">	<span class="class"><span class="keyword">enum</span> <span class="title">command_ret_t</span> <span class="title">rc</span> =</span> CMD_RET_SUCCESS;</span><br><span class="line">	<span class="type">cmd_tbl_t</span> *cmdtp;</span><br><span class="line"></span><br><span class="line">	<span class="comment">/* Look up command in command table */</span></span><br><span class="line">	cmdtp = find_cmd(argv[<span class="number">0</span>]);</span><br><span class="line">	<span class="keyword">if</span> (cmdtp == <span class="literal">NULL</span>) &#123;</span><br><span class="line">		<span class="built_in">printf</span>(<span class="string">&quot;Unknown command &#x27;%s&#x27; - try &#x27;help&#x27;\n&quot;</span>, argv[<span class="number">0</span>]);</span><br><span class="line">		<span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="comment">/* found - check max args */</span></span><br><span class="line">	<span class="keyword">if</span> (argc &gt; cmdtp-&gt;maxargs)</span><br><span class="line">		rc = CMD_RET_USAGE;</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">if</span> defined(CONFIG_CMD_BOOTD)</span></span><br><span class="line">	<span class="comment">/* avoid &quot;bootd&quot; recursion */</span></span><br><span class="line">	<span class="keyword">else</span> <span class="keyword">if</span> (cmdtp-&gt;cmd == do_bootd) &#123;</span><br><span class="line">		<span class="keyword">if</span> (flag &amp; CMD_FLAG_BOOTD) &#123;</span><br><span class="line">			<span class="built_in">puts</span>(<span class="string">&quot;&#x27;bootd&#x27; recursion detected\n&quot;</span>);</span><br><span class="line">			rc = CMD_RET_FAILURE;</span><br><span class="line">		&#125; <span class="keyword">else</span> &#123;</span><br><span class="line">			flag |= CMD_FLAG_BOOTD;</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"></span><br><span class="line">	<span class="comment">/* If OK so far, then do the command */</span></span><br><span class="line">	<span class="keyword">if</span> (!rc) &#123;</span><br><span class="line">		<span class="keyword">if</span> (ticks)</span><br><span class="line">			*ticks = get_timer(<span class="number">0</span>);</span><br><span class="line">		rc = cmd_call(cmdtp, flag, argc, argv);</span><br><span class="line">		<span class="keyword">if</span> (ticks)</span><br><span class="line">			*ticks = get_timer(*ticks);</span><br><span class="line">		*repeatable &amp;= cmdtp-&gt;repeatable;</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="keyword">if</span> (rc == CMD_RET_USAGE)</span><br><span class="line">		rc = cmd_usage(cmdtp);</span><br><span class="line">	<span class="keyword">return</span> rc;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>第 8 行（第 507 行）：调用函数 find_cmd 在命令表中找到指定的命令。</p>
<details class="folding-tag" blue><summary> 点击查看 find_cmd 函数 </summary>
              <div class='content'>
              <p>函数定义在 common&#x2F;command.c 文件中：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">cmd_tbl_t</span> *<span class="title function_">find_cmd</span><span class="params">(<span class="type">const</span> <span class="type">char</span> *cmd)</span></span><br><span class="line">&#123;</span><br><span class="line">	<span class="type">cmd_tbl_t</span> *start = ll_entry_start(<span class="type">cmd_tbl_t</span>, cmd);</span><br><span class="line">	<span class="type">const</span> <span class="type">int</span> len = ll_entry_count(<span class="type">cmd_tbl_t</span>, cmd);</span><br><span class="line">	<span class="keyword">return</span> find_cmd_tbl(cmd, start, len);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>参数 cmd 就是所查找的命令名字， uboot 中的命令表其实就是 cmd_tbl_t 结构体数组，通过函数 ll_entry_start 得到数组的第一个元素，也就是命令表起始地址。通过函数 ll_entry_count 得到数组长度，也就是命令表的长度。最终通过函数 find_cmd_tbl 在命令表中找到所需的命令，每个命令都有一个 name 成员，所以将参数 cmd 与命令表中每个成员的 name 字段都对比一下，如果相等的话就说明找到了这个命令，找到以后就返回这个命令。  </p>
              </div>
            </details>  

<p>第 34 行（第 533 行）：当 find_cmd 找到相应命令后，就会调用函数 cmd_call 来执行具体的命令 。</p>
<details class="folding-tag" blue><summary> 点击查看 cmd_call 函数 </summary>
              <div class='content'>
              <p>这个函数定义在 common&#x2F;command.c 中：</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Call a command function. This should be the only route in U-Boot to call</span></span><br><span class="line"><span class="comment"> * a command, so that we can track whether we are waiting for input or</span></span><br><span class="line"><span class="comment"> * executing a command.</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * @param cmdtp		Pointer to the command to execute</span></span><br><span class="line"><span class="comment"> * @param flag		Some flags normally 0 (see CMD_FLAG_.. above)</span></span><br><span class="line"><span class="comment"> * @param argc		Number of arguments (arg 0 must be the command text)</span></span><br><span class="line"><span class="comment"> * @param argv		Arguments</span></span><br><span class="line"><span class="comment"> * @return 0 if command succeeded, else non-zero (CMD_RET_...)</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="type">static</span> <span class="type">int</span> <span class="title function_">cmd_call</span><span class="params">(<span class="type">cmd_tbl_t</span> *cmdtp, <span class="type">int</span> flag, <span class="type">int</span> argc, <span class="type">char</span> * <span class="type">const</span> argv[])</span></span><br><span class="line">&#123;</span><br><span class="line">	<span class="type">int</span> result;</span><br><span class="line"></span><br><span class="line">	result = (cmdtp-&gt;cmd)(cmdtp, flag, argc, argv);</span><br><span class="line">	<span class="keyword">if</span> (result)</span><br><span class="line">		debug(<span class="string">&quot;Command failed, result=%d\n&quot;</span>, result);</span><br><span class="line">	<span class="keyword">return</span> result;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>在前面的分析中我们知道， cmd_tbl_t 的 cmd 成员就是具体的命令处理函数，所以第 16 行（第 494 行）调用 cmdtp 的 cmd 成员来处理具体的命令，返回值为命令的执行结果。  </p>
              </div>
            </details>

<p>第 39 行（第 538 行）：cmd_process 中会检测 cmd_tbl 的返回值，如果返回值为 CMD_RET_USAGE 的话就会调用 cmd_usage 函数输出命令的用法，其实就是输出 cmd_tbl_t 的 usage 成员变量。  </p>
<h1 id="三、uboot启动总结"><a href="#三、uboot启动总结" class="headerlink" title="三、uboot启动总结"></a><font size=3>三、uboot启动总结</font></h1><img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV05-uboot%E4%B8%8E%E5%86%85%E6%A0%B8/LV05-01-uboot-04-uboot%E5%90%AF%E5%8A%A8%E6%B5%81%E7%A8%8B/img/uboot_start-169061995797628.png" alt="uboot_start" style="zoom:50%;" />


    </div>

    
    
    

    <footer class="post-footer">




    <div>
        
            <div style="text-align:center;color: #ccc;font-size:14px;">
            ----------本文结束
            <i class="fas fa-fan fa-spin" style="color: #FF1493; font-size: 1rem"></i>
            感谢您的阅读----------
            </div>
        
    </div>





  
  <div class="my_post_copyright"> 
    <p><span>文章标题:</span><a href="/post/e99b9cbc.html">LV05-01-uboot-04-uboot启动流程</a></p>
    <p><span>文章作者:</span><a href="/" title="欢迎访问 《苏木》 的学习笔记">苏木</a></p>
    <p><span>发布时间:</span>2023年09月08日 - 19:22</p>
    <p><span>最后更新:</span>2025年06月14日 - 00:25</p>
    <p><span>原始链接:</span><a href="/post/e99b9cbc.html" title="LV05-01-uboot-04-uboot启动流程">https://sumumm.github.io/post/e99b9cbc.html</a></p>
    <p><span>许可协议:</span><i class="fab fa-creative-commons"></i> <a rel="license" href= "https://creativecommons.org/licenses/by-nc-nd/4.0/" target="_blank" title="Attribution-NonCommercial-NoDerivatives 4.0 International (CC BY-NC-ND 4.0)">署名-非商业性使用-禁止演绎 4.0 国际</a> 转载请保留原文链接及作者。</p>  
  </div>
  


          <div class="post-tags">
              <a href="/tags/ALPHA-LV05-uboot%E4%B8%8E%E5%86%85%E6%A0%B8/" rel="tag"><i class="fa fa-tag"></i> (ALPHA)LV05-uboot与内核</a>
          </div>

        

          <div class="post-nav">
            <div class="post-nav-item">
                <a href="/post/a7c5ba62.html" rel="prev" title="LV05-01-uboot-05-uboot启动内核">
                  <i class="fa fa-angle-left"></i> LV05-01-uboot-05-uboot启动内核
                </a>
            </div>
            <div class="post-nav-item">
                <a href="/post/d56430c4.html" rel="next" title="LV05-01-uboot-03-uboot相关命令">
                  LV05-01-uboot-03-uboot相关命令 <i class="fa fa-angle-right"></i>
                </a>
            </div>
          </div>
    </footer>
  </article>
</div>






</div>
  </main>

  <footer class="footer">
    <div class="footer-inner">

  <div class="copyright">
    &copy; 2017 – 
    <span itemprop="copyrightYear">2025</span>
    <span class="with-love">
      <i class="fa fa-heart"></i>
    </span>
    <span class="author" itemprop="copyrightHolder">苏木</span>
  </div>
<div class="wordcount">
  <span class="post-meta-item">
    <span class="post-meta-item-icon">
      <i class="fa fa-chart-line"></i>
    </span>
      <span>站点总字数：</span>
    <span title="站点总字数">3.7m</span>
  </span>
  <span class="post-meta-item">
    <span class="post-meta-item-icon">
      <i class="fa fa-coffee"></i>
    </span>
      <span>站点阅读时长 &asymp;</span>
    <span title="站点阅读时长">225:26</span>
  </span>
</div>




    <span id="sitetime"></span>
    <script defer language=javascript>
        function siteTime()
        {
            window.setTimeout("siteTime()", 1000);
            var seconds = 1000;
            var minutes = seconds * 60;
            var hours = minutes * 60;
            var days = hours * 24;
            var years = days * 365;
            var today = new Date();
            var todayYear = today.getFullYear();
            var todayMonth = today.getMonth()+1;
            var todayDate = today.getDate();
            var todayHour = today.getHours();
            var todayMinute = today.getMinutes();
            var todaySecond = today.getSeconds();
            /*==================================================
            Date.UTC() -- 返回date对象距世界标准时间(UTC)1970年1月1日午夜之间的毫秒数(时间戳)
            year        - 作为date对象的年份，为4位年份值
            month       - 0-11之间的整数，做为date对象的月份
            day         - 1-31之间的整数，做为date对象的天数
            hours       - 0(午夜24点)-23之间的整数，做为date对象的小时数
            minutes     - 0-59之间的整数，做为date对象的分钟数
            seconds     - 0-59之间的整数，做为date对象的秒数
            microseconds - 0-999之间的整数，做为date对象的毫秒数
            ==================================================*/
            var t1 = Date.UTC(2017, 
                              5, 
                              19, 
                              0, 
                              0, 
                              0); //北京时间
            var t2 = Date.UTC(todayYear,todayMonth,todayDate,todayHour,todayMinute,todaySecond);
            var diff = t2-t1;
            var diffYears = Math.floor(diff/years);
            var diffDays = Math.floor((diff/days)-diffYears*365);
            var diffHours = Math.floor((diff-(diffYears*365+diffDays)*days)/hours);
            var diffMinutes = Math.floor((diff-(diffYears*365+diffDays)*days-diffHours*hours)/minutes);
            var diffSeconds = Math.floor((diff-(diffYears*365+diffDays)*days-diffHours*hours-diffMinutes*minutes)/seconds);
            document.getElementById("sitetime").innerHTML="已在这里 "+diffYears+" 年 "+diffDays+" 天 "+diffHours+" 小时 "+diffMinutes+" 分钟 "+diffSeconds+" 秒";
        }
        siteTime();
    </script>



    </div>
  </footer>

  
  <div class="back-to-top" role="button" aria-label="返回顶部">
    <i class="fa fa-arrow-up fa-lg"></i>
    <span>0%</span>
  </div>
  <div class="reading-progress-bar"></div>

<noscript>
  <div class="noscript-warning">Theme NexT works best with JavaScript enabled</div>
</noscript>


  
  <script src="https://cdnjs.cloudflare.com/ajax/libs/animejs/3.2.1/anime.min.js" integrity="sha256-XL2inqUJaslATFnHdJOi9GfQ60on8Wx1C2H8DYiN1xY=" crossorigin="anonymous"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/next-theme-pjax/0.6.0/pjax.min.js" integrity="sha256-vxLn1tSKWD4dqbMRyv940UYw4sXgMtYcK6reefzZrao=" crossorigin="anonymous"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/fancyapps-ui/5.0.28/fancybox/fancybox.umd.js" integrity="sha256-ytMJGN3toR+a84u7g7NuHm91VIR06Q41kMWDr2pq7Zo=" crossorigin="anonymous"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/lozad.js/1.16.0/lozad.min.js" integrity="sha256-mOFREFhqmHeQbXpK2lp4nA3qooVgACfh88fpJftLBbc=" crossorigin="anonymous"></script>
<script src="/js/comments.js"></script><script src="/js/utils.js"></script><script src="/js/motion.js"></script><script src="/js/next-boot.js"></script><script src="/js/pjax.js"></script>

  <script src="https://cdnjs.cloudflare.com/ajax/libs/hexo-generator-searchdb/1.4.1/search.js" integrity="sha256-1kfA5uHPf65M5cphT2dvymhkuyHPQp5A53EGZOnOLmc=" crossorigin="anonymous"></script>
<script src="/js/third-party/search/local-search.js"></script>




  <script src="/js/third-party/fancybox.js"></script>

  <script src="/js/third-party/pace.js"></script>


  




  

  <script class="next-config" data-name="enableMath" type="application/json">true</script><script class="next-config" data-name="mathjax" type="application/json">{"enable":true,"tags":"none","js":{"url":"https://cdnjs.cloudflare.com/ajax/libs/mathjax/3.2.2/es5/tex-mml-chtml.js","integrity":"sha256-MASABpB4tYktI2Oitl4t+78w/lyA+D7b/s9GEP0JOGI="}}</script>
<script src="/js/third-party/math/mathjax.js"></script>


 
        <div id="click-show-text"
            data-mobile = false
            data-text = 富强,民主,文明,和谐,自由,平等,公正,法制,爱国,敬业,诚信,友善
            data-fontsize = 15px
            data-random= false>
        </div>
       

      
        <script async src=https://cdn.jsdelivr.net/npm/hexo-next-mouse-effect@latest/click/showText.js></script>
      

      
    




    <script async src="/js/fancybox_param.js"></script>





<!-- APlayer本体 -->



</body>
</html>
